External Node Classification using OpenLDAP – A Scalable solution for CFEngine, Puppet, Chef

Problem: You have a huge infrastructure with thousands of machines to manage. You already have a configuration management system or looking for one now (weird!!) and weighing what scalable tools you can use to specify role of each node, or a bunch of nodes

Solution: Puppet has hiera for this, if it’s scalable or not that’s debatable. People who are using it might be able to comment on this. But if you need something rock solid as a hierarchical database platform as well as massively scalable, I would advise you take a look at OpenLDAP.

Yes, you heard me right. OpenLDAP has come a long way since the Berkley DB days. Now you have LMDB/MDB (Lightening Memory Mapped DB) backend, which is modelled for high volume read queries. This is primary need for an External Node Classification (ENC) tool. Because using config management systems, we will periodically query ENC for roles. Apart from this, you can further index specific attributes, like cn, member, memberOf, etc.

Next thing, synchronization/replication. At this point, you do not want to write your own YAML based hierarchical DB which lacks sync/replication strategies. More over, you do not want to wait for 30 mins or so for the role/ENC DB update to take place. You need this to happen almost immediately. OpenLDAP provides sync-repl and delta-repl, which are full and incremntal replication, respectively. On top of this, it is event based. Plus you can customize OLC overlday to pinpoint exact attributes of objects that you want to put more focus on. In our case, these attributes would be “member” and “memberOf”.

Above all, scalability. I don’t think anybody doubts scalability of LDAP as a protocol. OpenLDAP has an architecture of Consumer (client as well as slaves) and Provider (essentially master) that can be leveraged to scale OpenLDAP further.

If you care, you do not have to run OpenLDAP solely on hardware. You can dockerize it (thanks RHEL7) or virtualize it traditionally, or run it along with config management systems. OpenLDAP itself is tiny, as long as you have enough disk space to have MDB database (it takes only 270MB to hold 60k hosts + ~900 groups, surprised? Don’t be!!)

You do not have to write codes to maintain integrity of your ENC DB, this is already provided by OpenLDAP in the name of “Referencial Integrity”. With this, whenever you add a host to a group, host’s memberOf attribute automatically gets updated. Remember memberOf attribute saves a hell lot of time while doing reverse lookup i.e “what groups is this host a part of?”

Now it’s time for demo.

What I needed:

1. OpenLDAP 2.4.40 (custom compiled with mdb and referential integrity)
2. Python 2.6/2.7 (haven’t tested in python 3, but it can be easily portable)
3. python-ldap and python-argparse modules
4. A cup of hot coffee

I am using CFEngine as config manager, but the same can be tested with Chef/Puppet and others, if need be.

Schema ldifs can be found in my github repository: https://github.com/soumyadipdm/enc-openldap

Here’s how I use it with CFEngine: https://github.com/soumyadipdm/cfengine_stuffs

I am using two OUs: hosts => for hosts
groups => for roles

You may follow steps mentioned in git repo to setup OpenLDAP for demo.

Here’s how it’s setup:

1. CFEngine promises.cf evaluates extract_raise_class shell script (execution can be delayed using a splay class, if your infrastructure does not see frequent role changes, once per 15-20 minutes execution is probably fine)
2. extract_raise_class in turn executes query-enc.py and results are written to /etc/roles_classes
3. raise_class is called upon by CFE, and this script just cat’s /etc/roles_classes file (this is to avoid execution of costly query-enc.py)

How query-enc.py is querying OpenLDAP:

In case of CFEngine, role discovery happens at the CFEngine client level. Hence, query-enc.py executed for reverse lookup to pull out all roles/groups that the host is a part of.

{unixuser@app01}[enc-openldap|23:59]-> ./query-enc.py hosts:generic_host
app02.local.net
app03.local.net
app04.local.net
app01.local.net
ubuntu.local.net
app05.local.net
app06.local.net

{unixuser@app01}[enc-openldap|00:01]-> ./query-enc.py reverse:`hostname -f`
generic_host
cfe_mps

{unixuser@app01}[enc-openldap|00:03]-> cat /etc/roles_classes
+generic_host
+cfe_mps

Advertisements

About admin_xor

Un*x/Linux junkie, loves to program, automate, automate and automate
This entry was posted in Uncategorized. Bookmark the permalink.

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