If you use any release of Puppet, open source or enterprise, you know that sometimes you have to cleanup leftover certnames, or nodes, when servers are decommissioned or simply ‘killed’ away. On this post, I will show you how to build a tool to constantly listen for node cleanup requests and delete them from Puppet. This is an example, you can take what I did here and do your own, improve, etc.

Use Case

An external system will send notifications when a machine has been destroyed, or decommissioned. After such notification is sent, Puppet should know about it and cleanup the SSL information about the machine and stop enforcing configuration management on it upon receipt.

The Code

The tool is written on Ruby and follows the same structure as the rest of my utilities. I stick to the same fashion of system design and re-use of code so I can work quickly on my concepts and validate them.

Message Queue

Install or re-use RabbitMQ on the puppetmaster or another server.

Configuration

On your working directory, create a folder called config and the file common.yaml:

---
 # RabbitMQ values
 mq_user: admin
 mq_pass: admin
 mq_server: ls0
 remove_channel: noderemoval

# Mongo values
 mongo_host: ls0
 mongo_port: 27017
 db: removednodes

The mongo values can be ignored, in my environment I set a record for each deletion.

The “middleware”

On your working directory, create a file named clean_node.rb. It is your main class:

#!/usr/bin/env ruby
# encoding: utf-8
# one change


require 'bunny'
require 'yaml'
require 'date'
require 'mongo'

class Cleannode

# load configs to use across the methods

fn = File.dirname(File.expand_path(__FILE__)) + '/config/common.yaml'
 config = YAML.load_file(fn)

# export common variables

@@datetime = DateTime.now()

# export the connection variables
 @@host = config['mq_server']
 @@mq_user = config['mq_user']
 @@mq_pass = config['mq_pass']

# export the channels to be created/used
 @@remove_ch = config['remove_channel']

# database values
 @@db = config['db']
 @@mongo_host = config['mongo_host']

# export connection to RabbitMQ
 @@conn = Bunny.new(:hostname => @@host,
 :user => @@mq_user,
 :password => @@mq_pass)


 def initialize()
 end

# define methods to use by server and clients
 # Post a message to remove a node that has been decommissioned
 def remove_node(certname)

@@conn.start

type = "REMOVE"
 message = type + "," + certname + "," + String(@@datetime)


 ch = @@conn.create_channel
 q = ch.queue(@@remove_ch)
 ch.default_exchange.publish(message, :routing_key => q.name)

puts " [x] Sent Removal Request to Puppet" + certname

@@conn.close


 end


end

 

The listener on the master

This is the actual piece that brings it all together and performs the deletion. Create a file called node_clean_listener.rb:

#!/usr/bin/env ruby
# encoding: utf-8

require "bunny"
require 'yaml'

fn = File.dirname(File.expand_path(__FILE__)) + '/config/common.yaml'
config = YAML.load_file(fn)

@@host = config['mq_server']
@@mq_user = config['mq_user']
@@mq_pass = config['mq_pass']
@@remove_ch = config['remove_channel']

conn = Bunny.new(:hostname => "#{@@host}",
 :user => "#{@@mq_user}",
 :password => "#{@@mq_pass}")
conn.start

ch = conn.create_channel
q = ch.queue("#{@@remove_ch}")

puts " [*] Waiting for messages in #{q.name}. To exit press CTRL+C"
q.subscribe(:block => true) do |delivery_info, properties, body|
 res = body.split(',')
 typ = res[0]
 certname = res[1]

puts " [x] Received #{body}"

#puts res
 puts typ
 puts certname

if typ == "REMOVE"
 #remove_job = fork do
 fork do
 puts "Removing node"
 exec "/opt/puppetlabs/bin/puppet cert clean #{certname}"
 end
 Process.detach
end

end

The exec cert clean command will have to change to node purge if using Puppet Enterprise.

Run this on the background to listen for deletion requests all the time.

Client test

Create a file called try.rb:

require "./clean_node"

# SIMPLE CLIENT TO TEST THE MIDDLEWARE
String host = "your-cert-name-to-delete"

# CREATE NEW OBJECT FROM CLEANNODE CLASS
d = Cleannode.new()
# TO CREATE A STATUS REQUEST FOR A SPECIFIC HOST
d.remove_node(host)

Replace the highlighted string in red with a node you want removed and run! The node will be deleted from Puppet.

The client piece can be any external system that will tell Puppet to remove the node.

Download the repo here and improve it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s