As with all tools, Red Hat Satellite offers a REST API. Ansible offers a simple way to access the API.

Background

Most of the programs and functions developed these days offer a REST API. Red Hat for example usually follows the “API first” methodology with most of the products these days, thus all functions of all programs can be accessed via REST API calls. For example I covered how to access the CloudForms REST API via Python.

While exploring a REST API via Python teaches a lot about the API and how to deal with all the basic tasks around REST communication, in daily enterprise business API calls should be automated – say hello to Ansible.

Ansible offers the URI module to work with generic HTTP requests. It offers various authentication modules, can pass general headers and provides ways to deal with different return codes and has a generic body field. Together with Ansible’s extensive variable features this makes the ideal combination for automated REST queries.

Setup

The setup is fairly simple: a Red Hat Satellite Server in a newer version (6.1 or newer), Ansible, and that’s it. The URI module in Satellite comes pre-installed.

Since the URI module accesses the target hosts via http, the actual host executing the http commands is the host on which the playbooks run. As a result, the host definition in the playbook needs to be localhost . In such case it doesn’t make sense to gather facts, either, so gather_facts: no can be set to save time.

In the module definition itself, it might make sense for test environments to ignore certification errors if the Satellite server certificate is not properly signed: validate_certs: no . Also, sometimes the Python library stumbles upon the status code 401 to initiate authentication. In that case, the option force_basic_auth: yes might help.

Last but not least, the API itself must be understood. The appropriate documentation is pretty helpful here: Red Hat Satellite API Guide. Especially the numerious examples at the end are a good start to build own REST calls in Ansible.

Getting values

Getting values via the REST API is pretty easy – the usual URL needs to be queried, the result is provided as JSON (in this case). The following example playbook asks the Satellite for the information about a given host. The output is reduced to the puppet modules, the number of modules is counted and the result is printed out.

$ cat api-get.yml --- - name: call API from Satellite hosts: localhost gather_facts: no vars: satelliteurl: satellite-server.example.com client: helium.example.com tasks: - name: get modules for given host from satellite uri: url: https://{{ satelliteurl }}/api/v2/hosts/{{ client }} method: GET user: admin password: password force_basic_auth: yes validate_certs: no register: restdata - name: output rest data debug: msg="{{ restdata.json.all_puppetclasses | count }}"

The execution of the playbook show the number of the installed Puppet modules:

$ ansible-playbook api-get.yml PLAY [call API from Satellite] ************************************************ TASK: [get ip and name from satellite] **************************************** ok: [localhost] TASK: [output rest data] ****************************************************** ok: [localhost] => { "msg": "8" } PLAY RECAP ******************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0

If the Jinja filter string | count is removed, the actual Puppet classes are listed.

Performing searches

Performing searches is simply another URL, and thus works the exact same way. The following playbook shows a search for all servers which are part of a given Puppet environment:

--- - name: call API from Satellite hosts: localhost gather_facts: no vars: satelliteurl: satellite-server.example.com clientenvironment: production tasks: - name: get Puppet environment from Satellite uri: url: https://{{ satelliteurl }}/api/v2/hosts/?search=environment={{ clientenvironment }} method: GET user: admin password: password force_basic_auth: yes validate_certs: no register: restdata - name: output rest data debug: msg="{{ restdata.json }}"

Changing configuration: POST

While querying the REST API can already be pretty interesting, automation requires the ability to change values as well. This can be done by changing the method: in the playbook to POST . Also, additional headers are necessary, and a body defining what data will be posted to Satellite.

The following example implements the example CURL call from the API guide mentioned above to add another architecture to Satellite:

$ cat api-post.yml --- - name: call API from Satellite hosts: localhost gather_facts: no vars: satelliteurl: satellite-server.example.com tasks: - name: set additional architecture in Satellite uri: url: https://{{ satelliteurl }}/api/architectures method: POST user: admin password: password force_basic_auth: yes validate_certs: no HEADER_Content-Type: application/json HEADER_Accept: :application/json,version=2 body: > {"architecture":{"name":"i686"}} register: restdata - name: output rest data debug: msg="{{ restdata }}"

The result can be looked up in the web interface: an architecture of the type i686 can now be found.

Update

Note that the body: > notation, folded scalars, makes it much easier to paste payload. If you are providing the payload without the closing bracket but on the same line, all the quotation marks need to be escaped:

body: "{\"architecture\":{\"name\":\"i686\"}}"

Conclusion

Ansible can easily access, query and control the Red Hat Satellite REST API and thus other REST APIs out there as well.

Ansible offers the possibility to automate almost any tool which expose a REST API. Together with the dynamic variable system results from one tool can easily be re-used to perform actions in another tool. That way even complex setups can be integrated with each other via Ansible rather easy.