Python wrapper for Puppet REST API
This package wraps the Puppet REST API to make it easier for Python scripts to integrate with Puppet. Persistent cache is used to reduce calls to the API.
Testing, feedback, and pull requests are welcome.
Kudos to my employer, Kloudless, for giving me permission to publish this.
>>> import puppet
>>> p = puppet.Puppet()
By default, the Puppet instance will use an unauthenticated SSL connection to localhost. A better example would be to use client authentication.
>>> p = puppet.Puppet(host='puppetmaster.example.com',
... port=8140,
... key_file='api-key.pem',
... cert_file='api-cert.pem',
... ssl_verify=True,
... cache_enabled=True,
... cache_file='pypuppet_cache',
... cache_backend='sqlite',
... cache_expire_after=3600)
Replace puppetmaster.example.com with the hostname of the puppet master and api-key.pem/api-cert.pem with the client SSL key/certificate files.
SSL verification is enabled by default.
Cache is enabled by default with multiple backend options (sqlite (default), memory, mongodb, redis). Default cache file will be stored at pypuppet_cache.sqlite
.
Given a puppet node called puppetnode.example.com,
>>> n = p.node('puppetnode.example.com')
>>> str(n)
'puppetnode.example.com'
>>> dir(n)
['__doc__', '__init__', '__module__', '__str__', 'catalog', 'certificate', 'certificate_status', 'certname', 'classes', 'environment', 'facts', 'node', 'parameters', 'requestor']
An optional node environment argument can be provided. Note that external node classifiers may override the requested environment.
>>> n_prod = p.node('puppetnode.example.com', environment='production')
If a node is not found, puppet.APIError
will be raised.
>>> n.certname
'puppetnode.example.com'
>>> type(n.classes)
<type 'dict'>
>>> n.environment
'production'
>>> type(n.facts)
<type 'dict'>
>>> n.facts['osfamily'] + "-" + n.facts['architecture']
'RedHat-x86_64'
>>> type(n.node)
<type 'dict'>
>>> sorted(n.node.keys())
['classes', 'environment', 'expiration', 'facts', 'name', 'parameters', 'time']
>>> type(n.parameters)
<type 'dict'>
method
>>> type(n.certificate())
<type 'str'>
>>> n.certificate().startswith('-----BEGIN CERTIFICATE')
True
method
>>> n.certificate_status()
'signed'
method (compiles and downloads catalog)
>>> catalog = n.catalog()
>>> type(catalog)
<type 'dict'>
>>> sorted(catalog.keys())
[u'classes', u'edges', u'environment', u'name', u'resources', u'tags', u'version']
In addition to the node method which creates a Node instance, there are three other methods in the Puppet instance.
certificates
: list certnames of known SSL certificatescertificate_requests
: list certnames of SSL certificate requestsfacts_search
: list nodes matching arguments of fact comparisons
Note that these methods return lists of strings, not lists of Node objects (which would be an expensive API call).
>>> [type(certnames[0]) for certnames in (p.certificates(),
... p.facts_search(('processorcount', 'ge', 1)))]
[<type 'str'>, <type 'str'>]
However, each string can be directly passed as the argument to the node method to create a Node object. A try-except
code block is recommended to gracefully handle exceptions caused by non-existent nodes.
>>> for certname in p.certificates():
... try:
... n_ = p.node(certname)
... break
... except puppet.APIError:
... # node probably does not exist
... continue
>>> dir(n_) == dir(n)
True
Each argument (if provided) of facts_search
must have two or three elements. The first argument is the name of the fact and the last argument is the string for comparison. If three arguments are provided, the second argument is the comparison type. The arguments are combined with boolean AND. Refer to the Puppet REST API documentation on facts search.
>>> if p.facts_search(('architecture', 'amd64'),('osfamily', 'Debian')):
... print(True)
True
>>> long_running_servers = p.facts_search(('uptime_days', 'gt', 100))
Direct invocation of the API can be done using the Requestor object's get
method, which takes four arguments:
resource
(required)key
(default:'no_key'
)environment
(default:'production'
)parser
'yaml'
(default),'pson'
, or's'
For example:
>>> crl = p.requestor.get('certificate_revocation_list', 'ca', parser='s')
>>> type(crl)
<type 'str'>
>>> crl.startswith('-----BEGIN X509 CRL')
True
Some other examples based on the Puppet REST API documentation:
p.requestor.get('resource', 'package/puppet')
p.requestor.get('resources', 'user')
Install the module using pip.
$ pip install https://github.com/daradib/pypuppet/archive/master.tar.gz
Alternatively, you can move the puppet directory to your Python PATH or wherever a script that imports it will be located. The only dependency is PyYAML.
The module defaults to using an unauthenticated SSL connection. You may want to create and use a client SSL key/certificate signed by the Puppet CA. On the puppet master (with root privileges):
$ puppet cert generate api
Replace api with another certname if desired.
This will generate $vardir/ssl/private_keys/api.pem
and $vardir/ssl/certs/api.pem
, which should be moved or otherwise made accessible to the user and host using pypuppet, if necessary. On Debian, $vardir
defaults to /var/lib/puppet
.
To allow access to the REST API, the puppet master auth.conf file needs to be changed. auth.conf.example
is included in this directory as an example.
Not currently supported:
- PUT requests (sending and signing certificate requests, sending facts and reports, putting files in the file bucket)
Copyright 2012-2013 Kloudless, Inc.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.