|  | ============================================================= | 
|  | reclass — recursive external node classification | 
|  | ============================================================= | 
|  | reclass is © 2007–2013 martin f. krafft <madduck@madduck.net> | 
|  | and available under the terms of the Artistic Licence 2.0 | 
|  | ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' | 
|  |  | 
|  | Please make sure to read the generic information in the README file first, or | 
|  | alongside this document. | 
|  |  | 
|  | Quick start with Salt | 
|  | ~~~~~~~~~~~~~~~~~~~~~ | 
|  | The following steps should get you up and running quickly. You will need to | 
|  | decide for yourself where to put your reclass inventory. This can be | 
|  | /etc/reclass, or it could be /srv/salt, for instance if /srv/salt/states is | 
|  | where your Salt file_roots live. The following shall assume the latter. | 
|  |  | 
|  | Or you can also just look into ./examples/salt of your reclass checkout, | 
|  | where the following steps have already been prepared. | 
|  |  | 
|  | /…/reclass refers to the location of your reclass checkout. | 
|  |  | 
|  | 1. Symlink /…/reclass/adapters/ansible to /etc/ansible/hosts (or | 
|  | ./hacking/hosts) | 
|  |  | 
|  | 2. Copy the two directories 'nodes' and 'classes' from the example | 
|  | subdirectory in the reclass checkout to /etc/ansible | 
|  |  | 
|  | If you prefer to put those directories elsewhere, you can create | 
|  | /etc/ansible/reclass-config.yml with contents such as | 
|  |  | 
|  | storage_type: yaml_fs | 
|  | nodes_uri: /srv/reclass/nodes | 
|  | classes_uri: /srv/reclass/classes | 
|  |  | 
|  | Note that yaml_fs is currently the only supported storage_type, and it's | 
|  | the default if you don't set it. | 
|  |  | 
|  | 3. Check out your inventory by invoking | 
|  |  | 
|  | ./hosts --list | 
|  |  | 
|  | which should return 5 groups in JSON-format, and each group has exactly | 
|  | one member 'localhost'. | 
|  |  | 
|  | 4. See the node information for 'localhost': | 
|  |  | 
|  | ./hosts --host localhost | 
|  |  | 
|  | This should print a set of keys and values, including a greeting, | 
|  | a colour, and a sub-class called 'RECLASS'. | 
|  |  | 
|  | 5. Execute some ansible commands, e.g. | 
|  |  | 
|  | ansible -i hosts \* --list-hosts | 
|  | ansible -i hosts \* -m ping | 
|  | ansible -i hosts \* -m debug -a 'msg="${greeting}"' | 
|  | ansible -i hosts \* -m setup | 
|  | ansible-playbook -i hosts test.yml | 
|  |  | 
|  | 6. You can also invoke reclass directly, which gives a slightly different | 
|  | view onto the same data, i.e. before it has been adapted for Ansible: | 
|  |  | 
|  | /…/reclass.py --pretty-print --inventory | 
|  | /…/reclass.py --pretty-print --nodeinfo localhost | 
|  |  | 
|  | Integration with Salt | 
|  | ~~~~~~~~~~~~~~~~~~~~~ | 
|  | The integration between reclass and Ansible is performed through an adapter, | 
|  | and needs not be of our concern too much. | 
|  |  | 
|  | However, Ansible has no concept of "nodes", "applications", "parameters", and | 
|  | "classes". Therefore it is necessary to explain how those correspond to | 
|  | Ansible. Crudely, the following mapping exists: | 
|  |  | 
|  | nodes         hosts | 
|  | classes       groups | 
|  | applications  playbooks | 
|  | parameters    host_vars | 
|  |  | 
|  | reclass does not provide any group_vars because of its node-centric | 
|  | perspective. While class definitions include parameters, those are inherited | 
|  | by the node definitions and hence become node_vars. | 
|  |  | 
|  | reclass also does not provide playbooks, nor does it deal with any of the | 
|  | related Ansible concepts, i.e. vars_files, vars, tasks, handlers, roles, etc.. | 
|  |  | 
|  | Let it be said at this point that you'll probably want to stop using | 
|  | host_vars, group_vars and vars_files altogether, and if only because you | 
|  | should no longer need them, but also because the variable precedence rules | 
|  | of Ansible are full of surprises, at least to me. | 
|  |  | 
|  | reclass' Ansible adapter massage the reclass output into Ansible-usable data, | 
|  | namely: | 
|  |  | 
|  | - Every class in the ancestry of a node becomes a group to Ansible. This is | 
|  | mainly useful to be able to target nodes during interactive use of | 
|  | Ansible, e.g. | 
|  |  | 
|  | ansible debiannode@wheezy -m command -a 'apt-get upgrade' | 
|  | → upgrade all Debian nodes running wheezy | 
|  |  | 
|  | ansible ssh.server -m command -a 'invoke-rc.d ssh restart' | 
|  | → restart all SSH server processes | 
|  |  | 
|  | ansible mailserver -m command -a 'tail -n1000 /var/log/mail.err' | 
|  | → obtain the last 1,000 lines of all mailserver error log files | 
|  |  | 
|  | The attentive reader might stumble over the use of singular words, whereas | 
|  | it might make more sense to address all 'mailserver*s*' with this tool. | 
|  | This is convention and up to you. I prefer to think of my node as | 
|  | a (singular) mailserver when I add 'mailserver' to its parent classes. | 
|  |  | 
|  | - Every entry in the list of a host's applications might well correspond to | 
|  | an Ansible playbook. Therefore, reclass creates a (Ansible-)group for | 
|  | every application, and adds '_hosts' to the name. This postfix can be | 
|  | configured with a CLI option (--applications-postfix) or in the | 
|  | configuration file (applications_postfix). | 
|  |  | 
|  | For instance, the ssh.server class adds the ssh.server application to | 
|  | a node's application list. Now the admin might create an Ansible playbook | 
|  | like so: | 
|  |  | 
|  | - name: SSH server management | 
|  | hosts: ssh.server_hosts              ← SEE HERE | 
|  | tasks: | 
|  | - name: install SSH package | 
|  | action: … | 
|  | … | 
|  |  | 
|  | There's a bit of redundancy in this, but unfortunately Ansible playbooks | 
|  | hardcode the nodes to which a playbook applies. | 
|  |  | 
|  | It's now trivial to apply this playbook across your infrastructure: | 
|  |  | 
|  | ansible-playbook ssh.server.yml | 
|  |  | 
|  | My suggested way to use Ansible site-wide is then to create a 'site' | 
|  | playbook that includes all the other playbooks (which shall hopefully be | 
|  | based on Ansible roles), and then to invoke Ansible like this: | 
|  |  | 
|  | ansible-playbook site.yml | 
|  |  | 
|  | or, if you prefer only to reconfigure a subset of nodes, e.g. all | 
|  | webservers: | 
|  |  | 
|  | ansible-playbook site.yml --limit webserver | 
|  |  | 
|  | Again, if the singular word 'webserver' puts you off, change the | 
|  | convention as you wish. | 
|  |  | 
|  | And if anyone comes up with a way to directly connect groups in the | 
|  | inventory with roles, thereby making it unnecessary to write playbook | 
|  | files (containing redundant information), please tell me! | 
|  |  | 
|  | - Parameters corresponding to a node become host_vars for that host. | 
|  |  | 
|  | It is possible to include Jinja2-style variables like you would in Ansible, | 
|  | in parameter values. This is especially powerful in combination with the | 
|  | recursive merging, e.g. | 
|  |  | 
|  | parameters: | 
|  | motd: | 
|  | greeting: Welcome to {{ ansible_fqdn }}! | 
|  | closing: This system is part of {{ realm }} | 
|  |  | 
|  | Now you just need to specify realm somewhere. The reference can reside in | 
|  | a parent class, while the variable is defined e.g. in the node. |