blob: b77a2c342f90b954dbfb6cfcd763993cea6280d8 [file] [log] [blame]
martin f. krafft8acd49d2013-08-26 21:22:25 +02001==========================
2Using reclass with Ansible
3==========================
4
martin f. kraffta06cffc2013-08-27 12:46:11 +02005.. warning::
6
7 I was kicked out of the Ansible community, presumably for `asking the wrong
8 questions`_, and therefore I have no interest in developing this adapter
9 anymore. If you use it and have changes, I will take your patch.
martin f. krafft8acd49d2013-08-26 21:22:25 +020010
11.. _asking the wrong questions: https://github.com/madduck/reclass/issues/6
12
13Quick start with Ansible
14------------------------
15The following steps should get you up and running quickly with |reclass| and
16`Ansible`_. Generally, we will be working in ``/etc/ansible``. However, if you
17are using a source-code checkout of Ansible, you might also want to work
18inside the ``./hacking`` directory instead.
19
20Or you can also just look into ``./examples/ansible`` of your |reclass|
21checkout, where the following steps have already been prepared.
22
23/…/reclass refers to the location of your |reclass| checkout.
24
martin f. krafftcc8851d2013-08-27 13:43:16 +020025.. todo::
26
27 With |reclass| now in Debian, as well as installable from source, the
28 following should be checked for path consistency
29
martin f. krafft8acd49d2013-08-26 21:22:25 +020030#. Complete the installation steps described in the :doc:`installation section
31 <install>`.
32
33#. Symlink ``/usr/share/reclass/reclass-ansible`` (or wherever your distro put
34 that file), or ``/…/reclass/reclass/adapters/ansible.py`` (if running from
35 source) to ``/etc/ansible/hosts`` (or ``./hacking/hosts``).
36
37#. Copy the two directories ``nodes`` and ``classes`` from the example
38 subdirectory in the |reclass| checkout to ``/etc/ansible``
39
40 If you prefer to put those directories elsewhere, you can create
41 ``/etc/ansible/reclass-config.yml`` with contents such as::
42
43 storage_type: yaml_fs
44 inventory_base_uri: /srv/reclass
45
46 Note that ``yaml_fs`` is currently the only supported ``storage_type``, and
47 it's the default if you don't set it.
48
49#. Check out your inventory by invoking
50
51 ::
52
53 $ ./hosts --list
54
55 which should return 5 groups in JSON format, and each group has exactly
56 one member ``localhost``.
57
584. See the node information for ``localhost``::
59
60 $ ./hosts --host localhost
61
62 This should print a set of keys and values, including a greeting,
63 a colour, and a sub-class called ``__reclas__``.
64
655. Execute some ansible commands, e.g.::
66
67 $ ansible -i hosts \* --list-hosts
68 $ ansible -i hosts \* -m ping
69 $ ansible -i hosts \* -m debug -a 'msg="${greeting}"'
70 $ ansible -i hosts \* -m setup
71 $ ansible-playbook -i hosts test.yml
72
736. You can also invoke |reclass| directly, which gives a slightly different
74 view onto the same data, i.e. before it has been adapted for Ansible::
75
76 $ /…/reclass/reclass.py --pretty-print --inventory
77 $ /…/reclass/reclass.py --pretty-print --nodeinfo localhost
78
79 Or, if |reclass| is properly installed, just use the |reclass| command.
80
81Integration with Ansible
82------------------------
83The integration between |reclass| and Ansible is performed through an adapter,
84and needs not be of our concern too much.
85
86However, Ansible has no concept of "nodes", "applications", "parameters", and
87"classes". Therefore it is necessary to explain how those correspond to
88Ansible. Crudely, the following mapping exists:
89
90================= ===============
91|reclass| concept Ansible concept
92================= ===============
93nodes hosts
94classes groups
95applications playbooks
96parameters host_vars
97================= ===============
98
99|reclass| does not provide any ``group_vars`` because of its node-centric
100perspective. While class definitions include parameters, those are inherited
101by the node definitions and hence become node_vars.
102
103|reclass| also does not provide playbooks, nor does it deal with any of the
104related Ansible concepts, i.e. ``vars_files``, vars, tasks, handlers, roles, etc..
105
106 Let it be said at this point that you'll probably want to stop using
107 ``host_vars``, ``group_vars`` and ``vars_files`` altogether, and if only
108 because you should no longer need them, but also because the variable
109 precedence rules of Ansible are full of surprises, at least to me.
110
111|reclass|' Ansible adapter massage the |reclass| output into Ansible-usable data,
112namely:
113
114- Every class in the ancestry of a node becomes a group to Ansible. This is
115 mainly useful to be able to target nodes during interactive use of
116 Ansible, e.g.::
117
118 $ ansible debiannode@wheezy -m command -a 'apt-get upgrade'
119 → upgrade all Debian nodes running wheezy
120
121 $ ansible ssh.server -m command -a 'invoke-rc.d ssh restart'
122 → restart all SSH server processes
123
124 $ ansible mailserver -m command -a 'tail -n1000 /var/log/mail.err'
125 → obtain the last 1,000 lines of all mailserver error log files
126
127 The attentive reader might stumble over the use of singular words, whereas
128 it might make more sense to address all ``mailserver*s*`` with this tool.
129 This is convention and up to you. I prefer to think of my node as
130 a (singular) mailserver when I add ``mailserver`` to its parent classes.
131
132- Every entry in the list of a host's applications might well correspond to
133 an Ansible playbook. Therefore, |reclass| creates a (Ansible-)group for
134 every application, and adds ``_hosts`` to the name. This postfix can be
135 configured with a CLI option (``--applications-postfix``) or in the
136 configuration file (``applications_postfix``).
137
138 For instance, the ssh.server class adds the ssh.server application to
139 a node's application list. Now the admin might create an Ansible playbook
140 like so::
141
142 - name: SSH server management
143 hosts: ssh.server_hosts ← SEE HERE
144 tasks:
145 - name: install SSH package
146 action: …
147
148
149 There's a bit of redundancy in this, but unfortunately Ansible playbooks
150 hardcode the nodes to which a playbook applies.
151
152 It's now trivial to apply this playbook across your infrastructure::
153
154 $ ansible-playbook ssh.server.yml
155
156 My suggested way to use Ansible site-wide is then to create a ``site.yml``
157 playbook that includes all the other playbooks (which shall hopefully be
158 based on Ansible roles), and then to invoke Ansible like this:
159
160 ansible-playbook site.yml
161
162 or, if you prefer only to reconfigure a subset of nodes, e.g. all
163 webservers::
164
165 $ ansible-playbook site.yml --limit webserver
166
167 Again, if the singular word ``webserver`` puts you off, change the
168 convention as you wish.
169
170 And if anyone comes up with a way to directly connect groups in the
171 inventory with roles, thereby making it unnecessary to write playbook
172 files (containing redundant information), please tell me!
173
174- Parameters corresponding to a node become ``host_vars`` for that host.
175
176Variable interpolation
177----------------------
178Ansible allows you to include `Jinja2`_-style variables in parameter values::
179
180 parameters:
181 motd:
182 greeting: Welcome to {{ ansible_fqdn }}!
183 closing: This system is part of {{ realm }}
184 dict_reference: {{ motd }}
185
186However, in resolving this, Ansible casts everything to a string, so in this
187example, ``dict_reference`` would be the string-representation of the
188dictionary under the ``motd`` key [#string_casts]_. To get at facts (such as
189``ansible_fqdn``), you still have to use this approach, but for pure parameter
190references, I strongly suggest to use |reclass| interpolation instead, as it
191supports deep references, does not clobber type information, and is more
192efficient anyway::
193
194 parameters:
195 motd:
196 greeting: Welcome to {{ ansible_fqdn }}!
197 closing: This system is part of ${realm}
198 dict_reference: ${motd}
199
200Now you just need to specify realm somewhere. The reference can reside in
201a parent class, while the variable is defined e.g. in the node definition.
202
203And as expected, ``dict_reference`` now points to a dictionary, not
204a string-representation thereof.
205
206.. [#string_casts] I pointed this out to Michael Dehaan, Ansible's chief
207 developer, but he denied this behaviour. When I tried to provide further
208 insights, I found myself banned from the mailing list, apparently because
209 I dared to point out flaws. If you care, you may look at
210 https://github.com/madduck/reclass/issues/6 for more information.
211
212.. include:: extrefs.inc
213.. include:: substs.inc