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 Red Hat Ansible Tower and the Survey feature. 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. See `Ansible Tower Job Template Surveys <https://docs.ansible.com/ansible-tower/latest/html/userguide/workflow_templates.html#surveys>`_ for more details. 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