Nagios is not the prettiest monitoring tool, but I do love it. There is something wonderful about all those little green squares telling me that everything is up and working.

I still remember the first time I set up a small Nagios system in my organization – I wrote every configuration file by hand and watched the green squares multiply as I added more hosts and services. It was fun at first, but as the system grew, editing all those configuration files by hand quickly became tedious. If you’re like me, the need to automate tedious tasks is overwhelming. That’s where the Landscape API comes in to make this simple.

At it’s heart, Landscape is a tool to help you manage your computing architecture. Landscape allows you to assign tags to every computer in your infrastructure. We’re going to use the Landscape API plus these tags to discover new computers and automatically configure them in Nagios. Add a computer to Landscape and it will automatically be added to Nagios with the appropriate hostgroup and services automatically configured.

The Landscape API
To use the Landscape API you need three pieces of information: the Landscape API endpoint URI, the user key, and the secret for your user.

To find your API credentials, log in to the Landscape web interface and click your username on the top right. Your API credentials are in the “API Access” section.

For this example, we’ll use the python API client provided with Landscape (NOTE, you must install the landscape-api package first). Here’s how to query for a list of all the computers in registered with Landscape:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from landscape_api.base import API

landscape_uri = "https://landscape.canonical.com/api/"
landscape_key = "43NW6OV71L32CSOPCJGX"
landscape_secret = "agBf3v267DqO8vtVRnzjseWfYdV4ueklj5a81iIT"
api = API(landscape_uri, landscape_key, landscape_secret)

api.get_computers()
[{u'access_group': u'server',
u'comment': u'',
u'hostname': u'appserv1',
u'id': 1,
u'last_exchange_time': None,
u'last_ping_time': u'2012-09-07T15:19:22Z',
u'reboot_required_flag': False,
u'tags': [u'lucid', u'server', u'webfarm'],
u'title': u'Application Server 1',
u'total_memory': 1024,
u'total_swap': 1024},
{u'access_group': u'server',
u'comment': u'',
u'hostname': u'appserv2',
u'id': 2,
u'last_exchange_time': None,
u'last_ping_time': u'2012-09-07T15:18:22Z',
u'reboot_required_flag': False,
u'tags': [u'nagios', u'precise', u'server', u'webserver'],
u'title': u'Application Server 2',
u'total_memory': 1024,
u'total_swap': 1024}]

 

For this example we will also apply a filter to the get_computers() call so we only get back computers tagged with “nagios” and we will ask for the networking information to be included in the response.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
api.get_computers(query="tag:nagios", with_networking=True)
[{u'access_group': u'server',
u'comment': u'',
u'hostname': u'appserv2',
u'id': 2,
u'last_exchange_time': None,
u'last_ping_time': u'2012-09-07T15:18:22Z',
u'network_devices': [{u'broadcast_address': u'192.168.0.255',
u'interface': u'eth0',
u'ip_address': u'192.168.0.61',
u'mac_address': u'00:1f:d9:ce:e1:69',
u'netmask': u'255.255.255.0'}],
u'reboot_required_flag': False,
u'tags': [u'nagios', u'precise', u'server', u'webserver'],
u'title': u'Application Server 2',
u'total_memory': 1024,
u'total_swap': 1024}]

 

Nagios Configuration
Once we get the computers we’re interested in, we need to add them to the Nagios config. This example creates a separate configuration file for each Landscape computer. These should all be stored in a directory which Nagios will read.

To tell Nagios to process all files in a directory (and it’s subdirectories) you use the cfg_dir= command like this:

1
cfg_dir=/path/to/host/files

 

Now all we have to do is create files in that directory for each host we get back from Landscape.

A Nagios host configuration file looks something like this:

1
2
3
4
5
6
7
define host{
    use          linux-host
    host_name    appserv2
    alias        Application Server 2
    address      192.168.0.61
    hostgroups   server, webserver
}

 

This file defines the host by specifying the host_name, alias, and address. The “use” parameter causes this host to inherit all the settings of the “linux-host” template. The “hostgroups” parameter puts this host under the “server” hostgroup and the “webserver” hostgroup.

The hostgroups define all the services that need to be monitored for any host that is part of the hostgroup. In this example, our host will get all services from the “server” hostgroup and the “webserver” hostgroup.

The Script
Now that we have the basics of talking to Landscape and writing config files for Nagios, we’re going to pull it all together.


The tags we get from landscape will be used to link the host to Nagios’s hostgroups. The script is designed to be run repeatedly to keep the host definitions up to date. In my setup I run it in a cronjob every five minutes – new hosts and tag changes are picked up fairly quickly.

The script is fairly simple. I’ll walk through it piece by piece. For the full listing with full comments and doc string see the end of the articale.

First, we import the libraries we need and define our configuration values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# List of valid hostgroups - Landscape tags not in this list are ignored.
# You must have a Nagios hostgroup defined for each entry in the list.
VALID_HOSTGROUPS = ["server", "webfarm"]

# A host template name to use for all the generated hosts. This host template
# must already be defined in your Nagios configuration.
HOST_TEMPLATE = "ubuntu-host"

# The directory to store the generated host files. It must be loaded into
# the Nagios config using a cfg_dir= command.
NAGIOS_HOST_DIRECTORY = "nagios"

 

Now we ask landscape for a list of all computers with the “nagios” tag set and update each computer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
api = API(LANDSCAPE_URI, LANDSCAPE_KEY, LANDSCAPE_SECRET)

try:
    # Get all computers tagged with "nagios"
    computers = api.get_computers(query="tag:nagios", with_network=True)
except errors.InvalidQueryError:
    # If there has never been a computer tagged "nagios" we get a
    # query error
    computers = []

# Update the nagios config for each computer
for computer in computers:
    update_computer(computer)

 

For each computer we extract the information Nagios needs. In this example we use the first IP address available for the host. If your network is more complex you may have to add some more smarts here to use the IP address reachable by Nagios. Once the config is generated we write it to a file in the Nagios hosts directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def update_computer(computer):
    """Generates a host config file for the computer and saves it to disk."""

    hostname = computer["hostname"]
    title = computer["title"]
    tags = computer["tags"]
    ips = [net["ip_address"] for net in computer["network_devices"]]

    # Filter out tags that aren't valid hostgroups
    hostgroups = set(VALID_HOSTGROUPS).intersection(set(tags))

    # If there is more than one IP address, use the first
    ip = ips[0]

    # Use the hostname as the nagios filename
    filename = "%s.cfg" % hostname
    filepath = os.path.join(NAGIOS_HOST_DIRECTORY, filename)

    # Generate the config file contents
    config = generate_nagios_config(HOST_TEMPLATE,
                                    hostname, title, ip, hostgroups)

    # Write out the config file
    with open(filepath, "wb") as f:
        f.write(config)

 

Here’s how the nagios host configuration file is generated:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def generate_nagios_config(template, hostname, alias, ip, hostgroups):
    """Generates the contents of a Nagios host config file."""
    lines = [
        "# Automatically generated from Landscape API data.",
        "# Any changes will be overwritten.",
        "define host{",
        " use %s" % template,
        " host_name %s" % hostname,
        " alias %s" % alias,
        " address %s" % ip,
        " hostgroups %s" % (", ".join(hostgroups)),
        "}"]
    return "\n".join(lines)

 

Finally, we ask nagios to reload the configuration files to see the new data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def reload_config():
    """
 Use Ubuntu init script to reload the Nagios configuration. Note that
 the configuration is checked before reloading to prevent errors from
 stopping the nagios process.
 """
    try:
        subprocess.check_output(["service", "nagios3", "reload"],
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        sys.stderr.write("FAILED RELOADING NAGIOS CONFIG\n")
        sys.stderr.write(e.output)
        sys.exit(1)

 

When the script is run, the new computer shows up immediately in Nagios:

And within a few minutes everything looks OK!

This simple example shows how you can integrate Landscape within your existing infrastructure. The Landscape API allows you to reach all your Ubuntu infrastructure using simple API commands from python or the command line.

We’ve barely scratched the surface of what the API can do. Check out the API Documentation to learn more.

The complete script is available here: http://paste.ubuntu.com/1200590/