-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
254 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
rundeck-puppetdb-nodes-plugin.zip | ||
deploy.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,97 @@ | ||
# rundeck-puppetdb-nodes | ||
Rundeck plugin to retrieve node definitions from PuppetDB | ||
Rundeck PuppetDB Nodes Plugin | ||
============================================= | ||
|
||
Description | ||
----------- | ||
This is a Resource Model Source plugin for [Rundeck][] Rundeck version 2.0 (or higher) that retrieves node definitions | ||
from PuppetDB. | ||
|
||
Developed in Python, it uses [python-requests][] library and Kerberos authentication to connect to the PuppetDB API. | ||
|
||
[RunDeck]: http://rundeck.org | ||
[python-requests]: http://docs.python-requests.org/en/latest/ | ||
|
||
Either the puppetdb API url, the foreman hostgroup, username, and the kerberos keytab path can be specified via plugin parameters on the project's configuration page. | ||
|
||
Parameters | ||
---------- | ||
`PuppetDB` - PuppetDB API URL following this format: `https://<SERVER>:<PORT>/<API VERSION>` | ||
|
||
> `https://my.puppet.db:2525/v3` | ||
`Foreman Hostgroup` - Specify a Foreman hosgroup to filter the query | ||
|
||
> `cloud_workflows` or `cloud_` | ||
`Kerberos user` - User to connect to PuppetDB | ||
|
||
`Kerberos keytab` - Path to user's keytab to authenticate | ||
|
||
![alt tag](images/config.png) | ||
|
||
Requirements | ||
------------ | ||
* The plugin requires Rundeck version 2.0 or higher. | ||
* python-requests v1.1.0-4 | ||
* python-requests-kerberos v0.5 ([important!!](https://bugzilla.redhat.com/show_bug.cgi?id=1169296)) | ||
|
||
Installation | ||
------------ | ||
Download the latest .ZIP from the [releases page](https://github.com/cernops/rundeck-puppetdb-nodes/releases) and copy it to `/var/lib/rundeck/libext/`. Restart the Rundeck service to be sure it gets the lastest changes. | ||
|
||
Next time you log in, you will see a new Resource Model Source called **PuppetDB Source** on the project's configuration page. | ||
|
||
Plugin Output example | ||
--------------------- | ||
``` | ||
scheduler-02.mydomain.com: | ||
hostname: scheduler-02.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/scheduler/server | ||
osName: SLC | ||
scheduler-01.mydomain.com: | ||
hostname: scheduler-01.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/scheduler/server | ||
osName: SLC | ||
loadbalancer-01.mydomain.com: | ||
hostname: loadbalancer-01.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/ha | ||
osName: SLC | ||
server-02.mydomain.com: | ||
hostname: server-02.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/server/production | ||
osName: SLC | ||
loadbalancer-02.mydomain.com: | ||
hostname: loadbalancer-02.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/ha | ||
osName: SLC | ||
server-qa-01.mydomain.com: | ||
hostname: server-qa-01.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/server/qa | ||
osName: SLC | ||
loadbalancer-qa-01.mydomain.com: | ||
hostname: loadbalancer-qa-01.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/ha | ||
osName: SLC | ||
server-qa-03.mydomain.com: | ||
hostname: server-qa-03.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/server | ||
osName: CentOS | ||
server-datastore.mydomain.com: | ||
hostname: server-datastore.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/datastore | ||
osName: CentOS | ||
server-qa-02.mydomain.com: | ||
hostname: server-qa-02.mydomain.com | ||
username: root | ||
tags: hostgroup=workflows/server | ||
osName: SLC | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 111 additions & 0 deletions
111
rundeck-puppetdb-nodes-plugin/contents/rundeck_puppetdb_nodes.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# Daniel Fernandez Rodriguez <[email protected]> | ||
|
||
from argparse import ArgumentParser | ||
from requests_kerberos import HTTPKerberosAuth | ||
|
||
import json | ||
import requests | ||
import subprocess | ||
import logging | ||
from collections import defaultdict | ||
|
||
def negociateKRBticket(keytab, username): | ||
kinit = '/usr/bin/kinit' | ||
kinit_args = [kinit, '-kt', keytab, username] | ||
kinit = subprocess.Popen(kinit_args) | ||
kinit.wait() | ||
|
||
def destroyKRBticket(): | ||
subprocess.call(["kdestroy"]) | ||
|
||
def getFactsPuppetDB(apiurl, facts, hostgroup): | ||
url ='%s/facts' % apiurl | ||
query_base = '["and",["or",%s],["in", "certname", ["extract", "certname", ["select-facts", ["and", ["=", "name", "hostgroup"], ["~", "value", "%s"]]]]]]' | ||
query_facts = ','.join(['["=","name","%s"]' % fact for fact in facts]) | ||
query = query_base % (query_facts, hostgroup) | ||
headers = {'Content-Type': 'application/json','Accept': 'application/json, version=2'} | ||
payload = {'query': query} | ||
logging.info("Getting facts from '%s', query: '%s'" % (url, query)) | ||
r = requests.get(url, params=payload, headers=headers, verify=False, auth=HTTPKerberosAuth()) | ||
if r.status_code == requests.codes.ok: | ||
logging.info("Request code: '%s'" % r.status_code) | ||
return json.loads(r.text) | ||
logging.error("The request failed with code '%s'" % r.status_code) | ||
return None | ||
|
||
def printNodesList(apiurl, hostgroup, factlist): | ||
''' | ||
Prints the nodes information in a supported format for Rundeck. | ||
''' | ||
facts = factlist.replace(',','').split() | ||
facts.extend(["operatingsystem", "operatingsystemrelease", "hostgroup"]) | ||
raw_data = getFactsPuppetDB(apiurl, facts, hostgroup) | ||
data = defaultdict(lambda: {}) | ||
if raw_data != None: | ||
for entry in raw_data: | ||
data[entry['certname']] = dict(data[entry['certname']].items() + [(entry['name'], entry['value'])]) | ||
|
||
logging.info("Printing node list using standard output...") | ||
for node in data.keys(): | ||
print ('%s:'%node) | ||
print (" "*4 + "hostname: " + node) | ||
print (" "*4 + "username: root") | ||
for fact in facts: | ||
if data[node].has_key(fact): | ||
print (" "*4 + fact + ": " + data[node][fact] ) | ||
logging.info("Node list printed successfully") | ||
else: | ||
logging.error("Fact list empty. Check PuppetDB connection params") | ||
|
||
def storeNodesList(apiurl, hostgroup, path): | ||
''' | ||
Saves the node list in a local file so rundeck can access it localy. | ||
''' | ||
operatingsystemfacts = getFactPuppetDB(apiurl, "operatingsystem", hostgroup) | ||
hostgroupfacts = getFactPuppetDB(apiurl, "hostgroup", hostgroup) | ||
logging.info("Saving node list in '%s'..." % path) | ||
if not (operatingsystemfacts == None or hostgroupfacts == None): | ||
with open("%s/nodes.yaml" % path, 'w') as file: | ||
for operatingsystem in operatingsystemfacts: | ||
global counter | ||
counter = counter + 1 | ||
file.write (operatingsystem['certname'] + ':\n') | ||
file.write (" "*4+"hostname: "+ operatingsystem['certname']+"\n") | ||
file.write (" "*4+"username: root\n") | ||
file.write(" "*4+"tags: ") | ||
for hostgroup in hostgroupfacts: | ||
if (operatingsystem['certname'] == hostgroup['certname']): | ||
file.write(" hostgroup="+ hostgroup['value']+"\n") | ||
file.write (" "*4+"osName: "+ operatingsystem['value']+"\n") | ||
logging.info("Node list saved successfully") | ||
else: | ||
logging.error("Fact list empty. Check PuppetDB connection params") | ||
|
||
def puppetdb_nodes_main(apiurl, hostgroup, keytab, username, factlist): | ||
negociateKRBticket(keytab, username) | ||
#storeNodesList(apiurl, hostgroup, path) | ||
printNodesList(apiurl, hostgroup, factlist) | ||
destroyKRBticket() | ||
|
||
def main(): | ||
parser = ArgumentParser(description="Get rundeck nodes from PuppetDB") | ||
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true") | ||
parser.add_argument("-d", "--debug", help="increase output to debug messages", action="store_true") | ||
parser.add_argument("--apiurl", help="PuppetDB API url (https://<SERVER>:<PORT>/<API VERSION>)", required=True) | ||
parser.add_argument("--hostgroup", help="Foreman hostgroup", required=True) | ||
parser.add_argument("--keytab", help="Keytab", required=True) | ||
parser.add_argument("--username", help="Username to connect to PuppetDB", required=True) | ||
parser.add_argument("--factlist", help="List of facts to retrieve for every node", required=True) | ||
parser.add_argument("--path", help="Path where the node list will be stored", nargs='?') | ||
|
||
args = parser.parse_args() | ||
|
||
if args.verbose: | ||
logging.basicConfig(level=logging.INFO) | ||
elif args.debug: | ||
logging.basicConfig(level=logging.DEBUG) | ||
|
||
puppetdb_nodes_main(args.apiurl, args.hostgroup, args.keytab, args.username, args.factlist) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#yaml plugin metadata | ||
|
||
name: PuppetDB source | ||
version: 1.0 | ||
rundeckPluginVersion: 1.0 | ||
author: Daniel Fernandez ([email protected]) | ||
date: 2015/02/12 | ||
providers: | ||
- name: rundeck-puppetdb-nodes | ||
service: ResourceModelSource | ||
title: PuppetDB source | ||
description: Queries PuppetDB to retrieve node definitions from a specific Foreman hostgroup | ||
resource-format: resourceyaml | ||
plugin-type: script | ||
script-interpreter: /usr/bin/python | ||
script-file: rundeck_puppetdb_nodes.py | ||
script-args: "-v --apiurl ${config.apiurl} --hostgroup ${config.hostgroup} --username ${config.username} --keytab ${config.keytab} --factlist ${config.factlist}" | ||
config: | ||
- name: apiurl | ||
type: String | ||
title: PuppetDB | ||
description: "PuppetDB API URL (https://<SERVER>:<PORT>/<API VERSION>). Ex: https://my.puppet.db:2525/v3" | ||
required: true | ||
- name: hostgroup | ||
type: String | ||
title: Hostgroup | ||
description: "Foreman Hostgroup" | ||
required: true | ||
- name: username | ||
type: String | ||
title: Username | ||
description: "User to connect to PuppetDB" | ||
required: true | ||
- name: keytab | ||
type: String | ||
title: Keytab | ||
description: "Path to user's keytab to authenticate" | ||
required: true | ||
- name: factlist | ||
type: String | ||
title: Fact list | ||
description: "List of facts to retrieve for every node" | ||
required: true | ||
default: "is_virtual" |