1
2.. _using_network_roles:
3
4*************************
5Use Ansible network roles
6*************************
7
8Roles are sets of Ansible defaults, files, tasks, templates, variables, and other Ansible components that work together. As you saw on :ref:`first_network_playbook`, moving from a command to a playbook makes it easy to run multiple tasks and repeat the same tasks in the same order. Moving from a playbook to a role makes it even easier to reuse and share your ordered tasks. You can look at :ref:`Ansible Galaxy <ansible_galaxy>`, which lets you share your roles and use others' roles, either directly or as inspiration.
9
10.. contents::
11   :local:
12
13Understanding roles
14===================
15
16So what exactly is a role, and why should you care? Ansible roles are basically playbooks broken up into a known file structure. Moving to roles from a playbook makes sharing, reading, and updating your Ansible workflow easier. Users can write their own roles. So for example, you don't have to write your own DNS playbook. Instead, you specify a DNS server and a role to configure it for you.
17
18To simplify your workflow even further, the Ansible Network team has written a series of roles for common network use cases. Using these roles means you don't have to reinvent the wheel. Instead of writing and maintaining your own ``create_vlan`` playbooks or roles, you can concentrate on designing, codifying and maintaining the parser templates that describe your network topologies and inventory, and let Ansible's network roles do the work. See the `network-related roles <https://galaxy.ansible.com/ansible-network>`_ on Ansible Galaxy.
19
20A sample DNS playbook
21---------------------
22
23To demonstrate the concept of what a role is, the example ``playbook.yml`` below is a single YAML file containing a two-task playbook.  This Ansible Playbook configures the hostname on a Cisco IOS XE device, then it configures the DNS (domain name system) servers.
24
25.. code-block:: yaml
26
27   ---
28   - name: configure cisco routers
29     hosts: routers
30     connection: ansible.netcommon.network_cli
31     gather_facts: no
32     vars:
33       dns: "8.8.8.8 8.8.4.4"
34
35     tasks:
36      - name: configure hostname
37        cisco.ios.ios_config:
38          lines: hostname {{ inventory_hostname }}
39
40      - name: configure DNS
41        cisco.ios.ios_config:
42          lines: ip name-server {{dns}}
43
44If you run this playbook using the ``ansible-playbook`` command, you'll see the output below.  This example used ``-l`` option to limit the playbook to only executing on the **rtr1** node.
45
46.. code-block:: bash
47
48   [user@ansible ~]$ ansible-playbook playbook.yml -l rtr1
49
50   PLAY [configure cisco routers] *************************************************
51
52   TASK [configure hostname] ******************************************************
53   changed: [rtr1]
54
55   TASK [configure DNS] ***********************************************************
56   changed: [rtr1]
57
58   PLAY RECAP *********************************************************************
59   rtr1                       : ok=2    changed=2    unreachable=0    failed=0
60
61
62This playbook configured the hostname and DNS servers.  You can verify that configuration on the Cisco IOS XE **rtr1** router:
63
64.. code-block:: bash
65
66   rtr1#sh run | i name
67   hostname rtr1
68   ip name-server 8.8.8.8 8.8.4.4
69
70Convert the playbook into a role
71---------------------------------
72
73The next step is to convert this playbook into a reusable role. You can create the directory structure manually, or you can use ``ansible-galaxy init`` to create the standard framework for a role.
74
75.. code-block:: bash
76
77   [user@ansible ~]$ ansible-galaxy init system-demo
78   [user@ansible ~]$ cd system-demo/
79   [user@ansible system-demo]$ tree
80   .
81   ├── defaults
82   │   └── main.yml
83   ├── files
84   ├── handlers
85   │   └── main.yml
86   ├── meta
87   │   └── main.yml
88   ├── README.md
89   ├── tasks
90   │   └── main.yml
91   ├── templates
92   ├── tests
93   │   ├── inventory
94   │   └── test.yml
95   └── vars
96     └── main.yml
97
98This first demonstration uses only the **tasks** and **vars** directories.  The directory structure would look as follows:
99
100.. code-block:: bash
101
102   [user@ansible system-demo]$ tree
103   .
104   ├── tasks
105   │   └── main.yml
106   └── vars
107       └── main.yml
108
109Next, move the content of the ``vars`` and ``tasks`` sections from the original Ansible Playbook into the role. First, move the two tasks into the ``tasks/main.yml`` file:
110
111.. code-block:: bash
112
113   [user@ansible system-demo]$ cat tasks/main.yml
114   ---
115   - name: configure hostname
116     cisco.ios.ios_config:
117       lines: hostname {{ inventory_hostname }}
118
119   - name: configure DNS
120     cisco.ios.ios_config:
121       lines: ip name-server {{dns}}
122
123Next, move the variables into the ``vars/main.yml`` file:
124
125.. code-block:: bash
126
127   [user@ansible system-demo]$ cat vars/main.yml
128   ---
129   dns: "8.8.8.8 8.8.4.4"
130
131Finally, modify the original Ansible Playbook to remove the ``tasks`` and ``vars`` sections and add the keyword ``roles``  with the name of the role, in this case ``system-demo``.  You'll have this playbook:
132
133.. code-block:: yaml
134
135   ---
136   - name: configure cisco routers
137     hosts: routers
138     connection: ansible.netcommon.network_cli
139     gather_facts: no
140
141     roles:
142       - system-demo
143
144To summarize, this demonstration now has a total of three directories and three YAML files.  There is the ``system-demo`` folder, which represents the role.  This ``system-demo`` contains two folders, ``tasks`` and ``vars``.  There is a ``main.yml`` is each respective folder.  The ``vars/main.yml`` contains the variables from ``playbook.yml``.  The ``tasks/main.yml`` contains the tasks from ``playbook.yml``.  The ``playbook.yml`` file has been modified to call the role rather than specifying vars and tasks directly.  Here is a tree of the current working directory:
145
146.. code-block:: bash
147
148   [user@ansible ~]$ tree
149   .
150   ├── playbook.yml
151   └── system-demo
152       ├── tasks
153       │   └── main.yml
154       └── vars
155           └── main.yml
156
157Running the playbook results in identical behavior with slightly different output:
158
159.. code-block:: bash
160
161   [user@ansible ~]$ ansible-playbook playbook.yml -l rtr1
162
163   PLAY [configure cisco routers] *************************************************
164
165   TASK [system-demo : configure hostname] ****************************************
166   ok: [rtr1]
167
168   TASK [system-demo : configure DNS] *********************************************
169   ok: [rtr1]
170
171   PLAY RECAP *********************************************************************
172   rtr1             : ok=2    changed=0    unreachable=0    failed=0
173
174As seen above each task is now prepended with the role name, in this case ``system-demo``.  When running a playbook that contains several roles, this will help pinpoint where a task is being called from.  This playbook returned ``ok`` instead of ``changed`` because it has identical behavior for the single file playbook we started from.
175
176As before, the playbook will generate the following configuration on a Cisco IOS-XE router:
177
178.. code-block:: bash
179
180   rtr1#sh run | i name
181   hostname rtr1
182   ip name-server 8.8.8.8 8.8.4.4
183
184
185This is why Ansible roles can be simply thought of as deconstructed playbooks. They are simple, effective and reusable.  Now another user can simply include the ``system-demo`` role instead of having to create a custom "hard coded" playbook.
186
187Variable precedence
188-------------------
189
190What if you want to change the DNS servers?  You aren't expected to change the ``vars/main.yml`` within the role structure. Ansible has many places where you can specify variables for a given play. See :ref:`playbooks_variables` for details on variables and precedence. There are actually 21 places to put variables.  While this list can seem overwhelming at first glance, the vast majority of use cases only involve knowing the spot for variables of least precedence and how to pass variables with most precedence. See :ref:`ansible_variable_precedence` for more guidance on where you should put variables.
191
192Lowest precedence
193^^^^^^^^^^^^^^^^^
194
195The lowest precedence is the ``defaults`` directory within a role.  This means all the other 20 locations you could potentially specify the variable will all take higher precedence than ``defaults``, no matter what.  To immediately give the vars from the ``system-demo`` role the least precedence, rename the ``vars`` directory to ``defaults``.
196
197.. code-block:: bash
198
199   [user@ansible system-demo]$ mv vars defaults
200   [user@ansible system-demo]$ tree
201   .
202   ├── defaults
203   │   └── main.yml
204   ├── tasks
205   │   └── main.yml
206
207Add a new ``vars`` section to the playbook to override the default behavior (where the variable ``dns`` is set to 8.8.8.8 and 8.8.4.4).  For this demonstration, set ``dns`` to 1.1.1.1, so ``playbook.yml`` becomes:
208
209.. code-block:: yaml
210
211   ---
212   - name: configure cisco routers
213     hosts: routers
214     connection: ansible.netcommon.network_cli
215     gather_facts: no
216     vars:
217       dns: 1.1.1.1
218     roles:
219       - system-demo
220
221Run this updated playbook on **rtr2**:
222
223.. code-block:: bash
224
225   [user@ansible ~]$ ansible-playbook playbook.yml -l rtr2
226
227The configuration on the **rtr2** Cisco router will look as follows:
228
229.. code-block:: bash
230
231   rtr2#sh run | i name-server
232   ip name-server 1.1.1.1
233
234The variable configured in the playbook now has precedence over the ``defaults`` directory.  In fact, any other spot you configure variables would win over the values in the ``defaults`` directory.
235
236Highest precedence
237^^^^^^^^^^^^^^^^^^
238
239Specifying variables in the ``defaults`` directory within a role will always take the lowest precedence, while specifying ``vars`` as extra vars with the ``-e`` or ``--extra-vars=`` will always take the highest precedence, no matter what.  Re-running the playbook with the ``-e`` option overrides both the ``defaults`` directory (8.8.4.4 and 8.8.8.8) as well as the newly created ``vars`` within the playbook that contains the 1.1.1.1 dns server.
240
241.. code-block:: bash
242
243   [user@ansible ~]$ ansible-playbook playbook.yml -e "dns=192.168.1.1" -l rtr3
244
245The result on the Cisco IOS XE router will only contain the highest precedence setting of 192.168.1.1:
246
247.. code-block:: bash
248
249   rtr3#sh run | i name-server
250   ip name-server 192.168.1.1
251
252How is this useful?  Why should you care?  Extra vars are commonly used by network operators to override defaults.  A powerful example of this is with the Job Template Survey feature on AWX or the :ref:`ansible_platform`.  It is possible through the web UI to prompt a network operator to fill out parameters with a Web form.  This can be really simple for non-technical playbook writers to execute a playbook using their Web browser.
253
254
255Update an installed role
256------------------------
257
258The Ansible Galaxy page for a role lists all available versions. To update a locally installed role to a new or different version, use the ``ansible-galaxy install`` command with the version and ``--force`` option. You may also need to manually update any dependent roles to support this version. See the role **Read Me** tab in Galaxy for dependent role minimum version requirements.
259
260.. code-block:: bash
261
262  [user@ansible]$ ansible-galaxy install mynamespace.my_role,v2.7.1 --force
263
264.. seealso::
265
266       `Ansible Galaxy documentation <https://galaxy.ansible.com/docs/>`_
267           Ansible Galaxy user guide
268