Making an Asset Tracking Tool with OpenLDAP
This post will be about how to use OpenLDAP to make an asset tracking
tool, suitable for keeping track of hardware, the software/apps being run
who owns them, who cares about them, who to notify in case of outages,
notes on the care and feeding and troubleshooting, remote console access,
and anything else you care to track. Some assembly is required, as is true
with most open source solutions, but then again, this one can easily be
tailored for whatever your needs are.
If all you want to see is the geeky details, you can
skip the next section where I give a little
background and my rationale for this tool.
At my job at SGI, we had a tool called DCSi
which I think stood for something like "Data Center System Information".
Don't ask me why the i was lower-cased - it always was when described to me
and it's just habit for me now - grin). I found it invaluable as the FNG
(friggin-new-guy) when I was on-call. When I got a page in the wee hours for
a server I'd never heard of, I could point my browser at DCSi, search for the
server name and get all sorts of useful information like where the machine
was (we're on call for servers all over the world), which building, which
room, which footprint in that room, what the server was for, who "owned" it,
etc. But it had "issues".
It was an internally developed tool - a collection or Perl CGIs with a Sybase
back-end - and the person who had developed it was long-since gone. Nobody
knew anything about it's guts, and it seemed to lose changes made to the data,
especially if more than one person edited the data. When we'd audit the
data center floor and note which servers were in which racks at what
footprints, noting serial numbers, asset tags, etc, invariably someone's
changes to DCSi would go missing. Bummer.
Then the datacenter in Mtn View, CA was closed and all the servers shipped
to Chippewa Falls, WI. And DCSi stopped working. And since it'd been
troublesome in the past with updates anyway, nobody wanted to spend any time
trying to fix it (it didn't help that almost everyone in a position to fix it
was getting laid off after the DCO move was done). So everyone took to editing
a spreadsheet stored in a common location as semi after semi hauled all the
servers to their new digs in WI each weekend. Needless to say, this didn't
work out very well and we had multiple versions of this spreadsheet with
conflicting info in it. Ugh.
I'd just built a new LDAP infrastructure for
SGI and had to make a custom schema for some
of the data we put in it, so it occurred to me that I could easily put the
same data in LDAP. And instead of making some one-off custom web app that
nobody had time to maintain, we could just use any LDAP browser/editor to
manage the data! I like Simple. And unlike the old DCSi app, we
could have multiple values for some of the items we tracked (ie, multiple
applications for a single server). Also unlike the old DCSi, if we wanted
to add something to what we tracked, it was a relatively easy change - no
application needed rewriting or SQL database changes needed.
So, I banged out a quick LDAP schema to track the same sorts of things we
had in the old DCSi application, added some more things I thought would be
useful, and presented it to the new boss (yeah, luckily I was kept on
after the servers were almost all moved to WI). After several demos to
various IT folks and application folks, it became the new DCSi. I also made
a very rudimentary PHP script to query it so we could easily hook it into
our nagios server. Each server in nagios now has a little red folder icon
next to it - click on it and it does an LDAP query and shows you all sorts
of useful info about that server - the sorts of things you're likely to
want to know when you just got paged about something on that server like
how to get remote console access, where it is, what it runs, links to
any build notes or documentation for the server or the apps it's running,
serial numbers, support contract info, etc.
We build our LDAP server from source. We do this for several reasons. I don't
really want to have to be tied to a specific version of OpenLDAP just
because some linux distro chooses it, or to have to upgrade the OS to get
a newer version. Plus it means we can build in support for the things we
care about and not build in support for the things we don't. It's
just reduces the number of possible security exploits a little. Besides, it's
pretty darn trivial to build an LDAP server.
I created a directory to build a particular version of our LDAP server, say
/data/ldap/build-v1.3 and inside there make a repository
directory that contains all the source .tar.gz files I'll be needing. Here's
an old version of that repository directory:
I also have a script that then builds all the binaries in the right order
with the right config options and installs everything in one location. The
idea here is that if I want to build a new set of binaries, it's easy - just
remove an old source tarball from the repository directory, replace it with
a newer version, re-run the extract-source script then optionally tweak the
build script to have a new install destination and rebuild a fresh set of
binaries without clobbering an existing set. We can quickly stop an LDAP
instance and re-start it with a new set of binaries and if we don't like
what we see, stop it and re-start it with the old binaries.
Anyway, here's my uber-simple
Note that the do-build.bash script is very simple. It does no error
checking. So if you're building for the first time or if you've just
updated a package in the repository be sure you run it saving the output
to a log file and check it carefully for errors, especially if you're on an
x86_64 or itanium platform. Opensource developers are still sometimes
releasing code that doesn't compile out of the box on the 64 bit platforms
so the next release of openssl or cyrus-sasl, for instance, might not compile
right on 64 bit linuxes. Usually this is fixed fairly quickly, but it's worth
watching out for.
Also, the do-build script, by default, builds BerkeleyDB with support for
their self-test which require tcl and tk development stuff to be installed. If
you want to omit this, though I always recommend re-running all the tests
when you switch versions of BerkeleyDB before assuming all will be well
with OpenLDAP, you can edit the do-build.bash script and comment out the
line that says --with-tcl=/usr/local/tcltk-8.4/lib --enable-test
So, once you've got LDAP binaries built (assuming you didn't start with
a pre-packaged set of binaries) you'll probably need to setup a config
file and a fresh database. There's several ways you can do this and
OpenLDAP has a new way of storing the config in a special LDAP DB rather
than using a config file. I'll leave all that as an exercise to the reader
and show a simple starting config. I'll also ignore things like running
N-Way LDAP replication, which you might want to do eventually.
If you're using pre-packaged binaries, you'll need to edit your slapd.conf
file to include my local.schema file and stop/restart your slapd daemon.
For the purposes of this example, let's assume our install destination was
/data/ldap/instance-v1.3 and that we're not keeping our LDAP instances
separate from the build instances. If you do want to keep them
separate (to make it easy to switch from one version of binaries to
another), then you'd use another directory for your LDAP instance like, maybe
/data/ldap/myldap and inside it you'd have your own etc and var directories
with your own slapd.conf file, schema files, and LDAP DB files).
Here's a very simple slapd.conf file. You'll want to
tailor it to your needs and you'll want to install it in your LDAP instance's
etc/openldap directory - in this example,
/data/ldap/instance-v1.3/etc/openldap. I'll also include an extra
schema file named
local.schema which should be in
/data/ldap/instance-v1.3/etc/openldap/schema/. Finally, here's an
initial.ldif file which we'll use to build a fresh
DB and a DB_CONFIG file which will tell OpenLDAP
about what BerkeleyDB settings we want to use. Put the initial.ldif file
in /data/ldap/instance-v1.3 and put DB_CONFIG in
I also usually create a README.runtime file in /data/ldap/instance-v1.3 (or
in my separate LDAP instance directories as needed if I'm keeping 'em
separate). It serves two purposes - it lets me drop notes in it on the
LDAP instance, can be "sourced" by a bash shell to load all the
environment variables to use the man pages or binaries of the instance, and
also shows a command line or two to start the LDAP server. Here's an
example README.runtime file. You'll want to
customize it to contain your LDAP server's hostname or IP in the LDAP
URL it specifies on the command to start it slapd.
Ok, almost there. You've created your directories, installed the slapd.conf,
local.schema, initial.ldif, DB_CONFIG, and README.runtime file. You've
customized them for your base dn, initial password for the cn=Manager
account, etc. Now, time
to create a fresh DB and start slapd. To do this, do:
slapadd -f ../../etc/openldap/slapd.conf <../../initial.ldif
It should say it imported those few records and you should now have some DB
files. If so, fire up slapd with:
$BTARGET/libexec/slapd -h "ldap://myhostname:389/" -f /data/ldap/ldap.sgi.com/etc/openldap/slapd.conf
If slapd starts, congratulations, you've built your very own LDAP server
from scratch. Pat yourself on the back and worry about things like LDAP
replication, SSL/TLS support for later. :-)
Now for the fun. You can use any LDAP browser editor tool that you like,
but at the time of this writing, I prefer Aapche Directory Studio. It's
interface may seem a little peculiar at first, but you can get used to it
quickly and it provides a powerful suite of tools for searching, modifying,
and reporting on data in an LDAP server (like the DCSi data). Fire up
Apache Directory Studio and connect to your brand new LDAP server as the
cn=Manager user. You should see there's already an ou=DCSi object. I'll
include some example structure beneath it but you can put whatever you'd
like in whatever locations you'd like. It's just LDAP - arrange your
data however you see fit! :-)
Having said that, here's how we use it. We have ou=Applications. This branch
contains dcsiApplication objects. These contain some info about the various
apps we run. Other objects will refer to these.
We also have ou=Platforms. This is where we'll put dcsiPlatform objects and
notes about them. If a particular platform always has (or even just usually
has) certain things in common like clock rates, number of CPUs, etc, you can
set them here. But we usually set stuff like that in specific server
records too. But like I said, you can put what ya like where ya like.
Then there's ou=Servers. This is where we put records for our live,
operating servers. We also have ou=Spares, ou=Available, and ou=Scrapped
branches. The idea is that if we take down a server but want to keep it
available for other uses (say, we power it off but leave it in the rack),
we put it in ou=Available. When someone needs a new server for some
purpose, search there first to see what's most quickly available. If we
shut down a server and unrack it but we keep it in storage for spare parts,
we'll put it's dcsiServer object in the ou=Spares branch. If we eventually
scrap hardware, we'll toss it in the ou=Scrapped branch so if someone later
comes asking what happened to server XYZ with asset tag ABC we can still find
it and say "It was scrapped on this date". But, again, you can layout the
structure of the data however you want. If you're gonna interface stuff with
nagios, I'd recommend putting all the devices/servers you're tracking under
one ou= branch or at least all the active stuff under one branch like
ou=Servers. It'll make querying for it from a script much easier.
here's a quick video showing me creating a
fresh DB, starting
OpenLDAP and connecting to it with my LDAP browser/editor of choice,
Apache DirectoryStudio, creating a new connection in DirectoryStudio
pointing to my new LDAP server and creating a few new records.
Here's another quick video showing
some simple ways to make new searches in DirectoryStudio.
And here's another quick video showing
how to make searches based on operational attributes like when a record
was created in DCSi (LDAP) or when it was last modified.
One thing I forgot to draw notice to in the video is that if a server is
known by many
different hostnames, say a webserver that hosts many virtual hosts, you
can add them all as additional dcsiServerName attribute values. That way if
someone searches by any of the virtual hostnames (ie, when they get a ticket
saying "website such 'n such is down") they can quickly see which physical
system is likely involved.
Also, there's a variety of attributes for platform objects and application
objects, not just servers. So you could have some emergency contacts and
owners specified for a server, but you could also have them specified for
Application objects that a server's dcsiHostedApplication attribute referred
If anyone asks, I've also made a few other quickie videos for my co-workers
showing some tips 'n tricks on using DirectoryStudio.
I could post them here if there was
interest so drop me an email if you'd like to see 'em. They're specific to
Apache DirectoryStudio, of course. But if you don't like this particular
tool, you can use any LDAP editor/browser.
I also made a script to query a server and populate a DCSi record with as much
info about a given server name as I can. It's moderately specific to SGI, but
it could be used as an example if someone wanted to make their own tool. I
also made one for scanning cisco devices and adding their info to DCSi
records. Drop me an email if you'd like a copy of either.
Lastly, I made a few simple PHP scripts for querying DCSi and showing the
sorts of attributes I figured an on-call person might be most interested in.
The intent was to make it easy to glue in this sort of info into nagios as
extra information links (which nagios displays as a red folder icon on host
pages and next to hostnames on host lists - click on the icon and it opens
a new browser tab to the PHP script querying for that server name). If anyone
wants it, I can drop an example of it here too.