Zimbra Monitoring with Zabbix

Zimbra already gathers pretty much any stat you could want, and it even comes with a zmstat-chart util to create graphs from them.

But what if you want to alert on a threshold value or look at trends longer than 24 hours, or combine arbitrary metrics?

This article will show you how to import the zmstats CSV data into Zabbix.


Zmstats uses a collection of scripts in /opt/zimbra/libexec/zmstat-*. to create CSV files in /opt/zimbra/zmstat/*.csv. Each day the files get gzipped and rotated to a YYYY-MM-DD directory. In fact, they will continue to grow over time and are not pruned.

There are a number of CSV files tracking metrics from low level OS operations to JVM garbage collection to application details.

  • allprocs.csv
  • convertd.csv
  • cpu.csv
  • df.csv
  • fd.csv
  • imap.csv
  • io-x.csv
  • io.csv
  • ldap.csv
  • mailboxd.csv
  • mysql.csv
  • proc.csv
  • soap.csv
  • sql.csv
  • sync.csv
  • threads.csv
  • vm.csv

For example, here are the first several of the 98 columns in mailboxd.csv.

11/07/2014 00:00:07,1,12541,1,1,12541,5,0.4,1,0.0
11/07/2014 00:01:07,1,3131,1,1,3131,22,0.22727272727272727,5,0.0
11/07/2014 00:02:07,1,6780,1,1,6780,5,0.6,0,0.0
11/07/2014 00:03:07,1,13557,1,1,13557,4,0.75,1,0.0
11/07/2014 00:04:07,2,42361,2,2,42361,6,1.3333333333333333,0,0.0
11/07/2014 00:05:07,1,33247,1,1,33247,4,0.25,3,0.0
11/07/2014 00:06:07,1,6368,1,1,6368,4,0.75,2,0.0
11/07/2014 00:07:07,2,45583,2,2,45583,5,0.6,1,0.0
11/07/2014 00:08:07,0,0,0,0,0,5,0.6,1,0.0

If you don not see any CSV files in /opt/zimbra/zmstat make sure zmstat is enabled and running.

[zimbra@zimbra-mbox-07 ~]$ zmprov getServer `zmhostname` zimbraServiceEnabled | grep stats
zimbraServiceEnabled: stats

[zimbra@zimbra-mbox-07 ~]$ zmstatctl status
Running: zmstat-proc
Running: zmstat-io
Running: zmstat-df
Running: zmstat-vm
Running: zmstat-allprocs
Running: zmstat-cpu
Running: zmstat-mysql
Running: zmstat-convertd
Running: zmstat-fd
Running: zmstat-io-x

Graphs from Zmstat

Zimbra already comes with the tools to create graphs from the CSV data and arrange them into HTML pages.


Reading the CSV

We can use python csv.DictReader to parse the CSV and manipulate the fields.

#!/usr/bin/env python
import csv
zmstatsfile = 'mailboxd.csv' 
zmstats     = csv.DictReader(open(zmstatsfile))
for line in zmstats:
    for key, val in line.iteritems():
        print "%s = %s" % (key, line[key])

From Zmstat to Zabbix

Zmstat collects a lot of the same metrics you would normally gather with zabbix_agent, and you may want to use them, but since mailboxd is a very large part of what defines Zimbra, it is a good place to start.

Create a Zabbix Template for Zimbra Mailboxd

The description for the columns in zmmailboxd.csv can be found on the wiki. There are 98 columns!

If we can create a python dictionary associating the description with the column name, then we can use those values to form a Zabbix item name and item key.

Add some more metadata like data type and using python lxml we can create a XML template for importing into Zabbix.

  • Here is a rough draft
#!/usr/bin/env python

# write out a zabbix template with item definitions

#from xml.etree import ElementTree as ET
    from lxml import etree
except ImportError:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")

from mailboxd_csv import mailboxd

# prefix to the 'key' string of the items
item_key_prefix = 'zimbra'

root          = etree.Element('root')

zabbix_export = etree.SubElement(root, 'zabbix_export')
etree.SubElement(zabbix_export, 'version').text = '2.0'
etree.SubElement(zabbix_export, 'date').text    = '2014-11-05T11:22:33Z'

groups        = etree.SubElement(zabbix_export, 'groups')
group         = etree.SubElement(groups, 'group')
name          = etree.SubElement(group, 'name').text = 'Zimbra'

applications  = etree.SubElement(zabbix_export, 'applications')
application   = etree.SubElement(applications, 'application')
name          = etree.SubElement(application, 'name').text = 'Zimbra'

templates     = etree.SubElement(zabbix_export, 'templates')
template      = etree.SubElement(templates, 'template')
items         = etree.SubElement(template, 'items')

for zkey, zname in mailboxd.iteritems():
    item = etree.SubElement(items, 'item')
    etree.SubElement(item, 'key').text  = '.'.join([item_key_prefix, zkey])
    etree.SubElement(item, 'name').text = zname

etree.SubElement(template, 'discovery_rules')
etree.SubElement(template, 'macros')
etree.SubElement(template, 'screens')
triggers     = etree.SubElement(zabbix_export, 'triggers')
etree.SubElement(triggers, 'trigger')

  • And here is some output