diff --git a/LibreNMS/Util/StringHelpers.php b/LibreNMS/Util/StringHelpers.php
index 0191e9f686..9254b1387d 100644
--- a/LibreNMS/Util/StringHelpers.php
+++ b/LibreNMS/Util/StringHelpers.php
@@ -75,6 +75,7 @@ class StringHelpers
'freeradius' => 'FreeRADIUS',
'pi-hole' => 'pi-hole',
'zfs' => 'ZFS',
+ 'mailcow-postfix' => 'mailcow-dockerized postfix',
];
return isset($replacements[$string]) ? $replacements[$string] : ucwords(str_replace(['_', '-'], ' ', $string));
diff --git a/doc/Extensions/Applications.md b/doc/Extensions/Applications.md
index 14ce07f767..7ab18e65d7 100644
--- a/doc/Extensions/Applications.md
+++ b/doc/Extensions/Applications.md
@@ -99,6 +99,7 @@ by following the steps under the `SNMP Extend` heading.
1. [FreeRADIUS](#freeradius) - SNMP extend, Agent
1. [Freeswitch](#freeswitch) - SNMP extend, Agent
1. [GPSD](#gpsd) - SNMP extend, Agent
+1. [Mailcow-dockerized postfix](#mailcow-dockerized-postfix) - SNMP extend
1. [Mailscanner](#mailscanner) - SNMP extend
1. [Mdadm](#mdadm) - SNMP extend
1. [Memcached](#memcached) - SNMP extend
@@ -739,6 +740,30 @@ You may need to configure `$server` or `$port`.
Verify it is working by running `/usr/lib/check_mk_agent/local/gpsd`
+# mailcow-dockerized postfix
+
+## SNMP Extend
+
+1: Download the script into the desired host.
+
+```
+wget https://github.com/librenms/librenms-agent/master/snmp/mailcow-dockerized-postfix -O /etc/snmp/mailcow-dockerized-postfix
+```
+
+2: Run `chmod +x /etc/snmp/mailcow-dockerized-postfix`
+
+3: Edit your snmpd.conf file (usually /etc/snmp/snmpd.conf) and add:
+
+```
+extend mailcow-postfix /etc/snmp/mailcow-dockerized-postfix
+```
+
+4: Restart snmpd on your host
+
+The application should be auto-discovered as described at the top of
+the page. If it is not, please follow the steps set out under `SNMP
+Extend` heading top of page.
+
# Mailscanner
## SNMP Extend
diff --git a/includes/html/graphs/application/mailcow-postfix_domains.inc.php b/includes/html/graphs/application/mailcow-postfix_domains.inc.php
new file mode 100644
index 0000000000..b83714b5da
--- /dev/null
+++ b/includes/html/graphs/application/mailcow-postfix_domains.inc.php
@@ -0,0 +1,51 @@
+
+*/
+
+require 'includes/html/graphs/common.inc.php';
+
+$scale_min = 0;
+$colours = 'mixed';
+$unit_text = 'Domain(s)';
+$unitlen = 10;
+$bigdescrlen = 25;
+$smalldescrlen = 25;
+$dostack = 0;
+$printtotal = 0;
+$addarea = 1;
+$transparency = 33;
+$rrd_filename = rrd_name($device['hostname'], array('app', $app['app_type'], $app['app_id']));
+
+$array = array(
+ 'senders' => array('descr' => 'Sender(s)', 'colour' => '11d3f5',),
+ 'sendinghostsdomains' => array('descr' => 'Sender domain(s)', 'colour' => '11abf2',),
+ 'recipients' => array('descr' => 'Recipient(s)', 'colour' => 'f78f19',),
+ 'recipienthostsdomains' => array('descr' => 'Recipient domain(s)', 'colour' => 'e0001a',),
+);
+
+$i = 0;
+
+if (rrdtool_check_rrd_exists($rrd_filename)) {
+ foreach ($array as $ds => $var) {
+ $rrd_list[$i]['filename'] = $rrd_filename;
+ $rrd_list[$i]['descr'] = $var['descr'];
+ $rrd_list[$i]['ds'] = $ds;
+ $rrd_list[$i]['colour'] = $var['colour'];
+ $i++;
+ }
+} else {
+ echo "file missing: $rrd_filename";
+}
+
+require 'includes/html/graphs/generic_v3_multiline.inc.php';
diff --git a/includes/html/graphs/application/mailcow-postfix_emails.inc.php b/includes/html/graphs/application/mailcow-postfix_emails.inc.php
new file mode 100644
index 0000000000..3ac710b225
--- /dev/null
+++ b/includes/html/graphs/application/mailcow-postfix_emails.inc.php
@@ -0,0 +1,55 @@
+
+*/
+
+require 'includes/html/graphs/common.inc.php';
+
+$scale_min = 0;
+$colours = 'mixed';
+$unit_text = 'E-mail(s)';
+$unitlen = 10;
+$bigdescrlen = 20;
+$smalldescrlen = 20;
+$dostack = 0;
+$printtotal = 0;
+$addarea = 1;
+$transparency = 33;
+$rrd_filename = rrd_name($device['hostname'], array('app', $app['app_type'], $app['app_id']));
+
+$array = array(
+ 'received' => array('descr' => 'Received', 'colour' => '75a832',),
+ 'delivered' => array('descr' => 'Delivered', 'colour' => '00d644',),
+ 'forwarded' => array('descr' => 'Forwarded', 'colour' => 'ccff99',),
+ 'deferred' => array('descr' => 'Deferred', 'colour' => 'ffcc66',),
+ 'bounced' => array('descr' => 'Bounced', 'colour' => 'cc6600',),
+ 'rejected' => array('descr' => 'Rejected', 'colour' => 'cc0000',),
+ 'held' => array('descr' => 'Held', 'colour' => '3366cc',),
+ 'discarded' => array('descr' => 'Discarded', 'colour' => '1a1a1a',),
+);
+
+$i = 0;
+
+if (rrdtool_check_rrd_exists($rrd_filename)) {
+ foreach ($array as $ds => $var) {
+ $rrd_list[$i]['filename'] = $rrd_filename;
+ $rrd_list[$i]['descr'] = $var['descr'];
+ $rrd_list[$i]['ds'] = $ds;
+ $rrd_list[$i]['colour'] = $var['colour'];
+ $i++;
+ }
+} else {
+ echo "file missing: $rrd_filename";
+}
+
+require 'includes/html/graphs/generic_v3_multiline.inc.php';
diff --git a/includes/html/graphs/application/mailcow-postfix_traffic.inc.php b/includes/html/graphs/application/mailcow-postfix_traffic.inc.php
new file mode 100644
index 0000000000..e911ccfc2a
--- /dev/null
+++ b/includes/html/graphs/application/mailcow-postfix_traffic.inc.php
@@ -0,0 +1,49 @@
+
+*/
+
+require 'includes/html/graphs/common.inc.php';
+
+$scale_min = 0;
+$colours = 'mixed';
+$unit_text = 'Traffic';
+$unitlen = 15;
+$bigdescrlen = 25;
+$smalldescrlen = 25;
+$dostack = 0;
+$printtotal = 0;
+$addarea = 1;
+$transparency = 33;
+$rrd_filename = rrd_name($device['hostname'], array('app', $app['app_type'], $app['app_id']));
+
+$array = array(
+ 'bytesreceived' => array('descr' => 'bytes Received', 'colour' => '4CB24C',),
+ 'bytesdelivered' => array('descr' => 'bytes Delivered', 'colour' => 'BF3F3F',),
+);
+
+$i = 0;
+
+if (rrdtool_check_rrd_exists($rrd_filename)) {
+ foreach ($array as $ds => $var) {
+ $rrd_list[$i]['filename'] = $rrd_filename;
+ $rrd_list[$i]['descr'] = $var['descr'];
+ $rrd_list[$i]['ds'] = $ds;
+ $rrd_list[$i]['colour'] = $var['colour'];
+ $i++;
+ }
+} else {
+ echo "file missing: $rrd_filename";
+}
+
+require 'includes/html/graphs/generic_v3_multiline.inc.php';
diff --git a/includes/html/pages/apps.inc.php b/includes/html/pages/apps.inc.php
index 1771428c6d..8c5c98290e 100644
--- a/includes/html/pages/apps.inc.php
+++ b/includes/html/pages/apps.inc.php
@@ -315,6 +315,11 @@ $graphs['asterisk'] = array(
'sip',
'iax2',
);
+$graphs['mailcow-postfix'] = array(
+ 'emails',
+ 'traffic',
+ 'domains',
+);
echo '
';
echo '
';
echo "
Apps » ";
diff --git a/includes/html/pages/device/apps/mailcow-postfix.inc.php b/includes/html/pages/device/apps/mailcow-postfix.inc.php
new file mode 100644
index 0000000000..3ccf008519
--- /dev/null
+++ b/includes/html/pages/device/apps/mailcow-postfix.inc.php
@@ -0,0 +1,46 @@
+.
+*
+* @package LibreNMS
+* @link http://librenms.org
+* @copyright 2020 LibreNMS
+* @author Cercel Valentin
+*/
+
+$graphs = array(
+ 'mailcow-postfix_emails' => 'E-mail(s)',
+ 'mailcow-postfix_traffic' => 'Traffic',
+ 'mailcow-postfix_domains' => 'Domain(s)',
+);
+
+foreach ($graphs as $key => $text) {
+ $graph_type = $key;
+ $graph_array['height'] = '100';
+ $graph_array['width'] = '215';
+ $graph_array['to'] = \LibreNMS\Config::get('time.now');
+ $graph_array['id'] = $app['app_id'];
+ $graph_array['type'] = 'application_' . $key;
+
+ echo '
+
+
' . $text . '
+
+
+
';
+ include 'includes/html/print-graphrow.inc.php';
+ echo '
';
+ echo '
';
+ echo '
';
+}
diff --git a/includes/polling/applications/mailcow-postfix.inc.php b/includes/polling/applications/mailcow-postfix.inc.php
new file mode 100644
index 0000000000..094fa80b0c
--- /dev/null
+++ b/includes/polling/applications/mailcow-postfix.inc.php
@@ -0,0 +1,78 @@
+.
+*
+* @package LibreNMS
+* @link http://librenms.org
+* @copyright 2020 LibreNMS
+* @author Cercel Valentin
+*/
+
+use LibreNMS\Exceptions\JsonAppException;
+use LibreNMS\RRD\RrdDefinition;
+
+$name = 'mailcow-postfix';
+$app_id = $app['app_id'];
+
+d_echo($name);
+
+try {
+ $mailcow_postfix = json_app_get($device, $name);
+} catch (JsonAppException $e) {
+ echo PHP_EOL . $name . ':' . $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
+ update_application($app, $e->getCode() . ':' . $e->getMessage(), []); // Set empty metrics and error message
+ return;
+}
+
+$rrd_name = array('app', $name, $app_id);
+
+$rrd_def = RrdDefinition::make()
+ ->addDataset('received', 'GAUGE', 0)
+ ->addDataset('delivered', 'GAUGE', 0)
+ ->addDataset('forwarded', 'GAUGE', 0)
+ ->addDataset('deferred', 'GAUGE', 0)
+ ->addDataset('bounced', 'GAUGE', 0)
+ ->addDataset('rejected', 'GAUGE', 0)
+ ->addDataset('rejectwarnings', 'GAUGE', 0)
+ ->addDataset('held', 'GAUGE', 0)
+ ->addDataset('discarded', 'GAUGE', 0)
+ ->addDataset('bytesreceived', 'GAUGE', 0)
+ ->addDataset('bytesdelivered', 'GAUGE', 0)
+ ->addDataset('senders', 'GAUGE', 0)
+ ->addDataset('sendinghostsdomains', 'GAUGE', 0)
+ ->addDataset('recipients', 'GAUGE', 0)
+ ->addDataset('recipienthostsdomains', 'GAUGE', 0);
+
+
+$fields = array(
+ 'received' => $mailcow_postfix['data']['received'],
+ 'delivered' => $mailcow_postfix['data']['delivered'],
+ 'forwarded' => $mailcow_postfix['data']['forwarded'],
+ 'deferred' => $mailcow_postfix['data']['deferred'],
+ 'bounced' => $mailcow_postfix['data']['bounced'],
+ 'rejected' => $mailcow_postfix['data']['rejected'],
+ 'rejectwarnings' => $mailcow_postfix['data']['rejectwarnings'],
+ 'held' => $mailcow_postfix['data']['held'],
+ 'discarded' => $mailcow_postfix['data']['discarded'],
+ 'bytesreceived' => $mailcow_postfix['data']['bytesreceived'],
+ 'bytesdelivered' => $mailcow_postfix['data']['bytesdelivered'],
+ 'senders' => $mailcow_postfix['data']['senders'],
+ 'sendinghostsdomains' => $mailcow_postfix['data']['sendinghostsdomains'],
+ 'recipients' => $mailcow_postfix['data']['recipients'],
+ 'recipienthostsdomains' => $mailcow_postfix['data']['recipienthostsdomains'],
+);
+
+$tags = compact('name', 'app_id', 'rrd_name', 'rrd_def');
+data_update($device, 'app', $tags, $fields);
+update_application($app, 'OK', $fields);
diff --git a/tests/data/linux_mailcow-dockerized-v1.json b/tests/data/linux_mailcow-dockerized-v1.json
new file mode 100644
index 0000000000..aabf1b753b
--- /dev/null
+++ b/tests/data/linux_mailcow-dockerized-v1.json
@@ -0,0 +1,121 @@
+{
+ "applications": {
+ "discovery": {
+ "applications": [
+ {
+ "app_type": "mailcow-postfix",
+ "app_state": "UNKNOWN",
+ "discovered": 1,
+ "app_state_prev": null,
+ "app_status": "",
+ "app_instance": ""
+ }
+ ],
+ "application_metrics": []
+ },
+ "poller": {
+ "applications": [
+ {
+ "app_type": "mailcow-postfix",
+ "app_state": "OK",
+ "discovered": 1,
+ "app_state_prev": "UNKNOWN",
+ "app_status": "",
+ "app_instance": ""
+ }
+ ],
+ "application_metrics": [
+ {
+ "metric": "bounced",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "bytesdelivered",
+ "value": 128364,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "bytesreceived",
+ "value": 64182,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "deferred",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "delivered",
+ "value": 4,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "discarded",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "forwarded",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "held",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "received",
+ "value": 2,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "recipienthostsdomains",
+ "value": 2,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "recipients",
+ "value": 2,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "rejected",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "rejectwarnings",
+ "value": 0,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "senders",
+ "value": 2,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ },
+ {
+ "metric": "sendinghostsdomains",
+ "value": 2,
+ "value_prev": null,
+ "app_type": "mailcow-postfix"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/snmpsim/linux_mailcow-dockerized-v1.snmprec b/tests/snmpsim/linux_mailcow-dockerized-v1.snmprec
new file mode 100644
index 0000000000..41f72c0cac
--- /dev/null
+++ b/tests/snmpsim/linux_mailcow-dockerized-v1.snmprec
@@ -0,0 +1,9 @@
+1.3.6.1.2.1.1.1.0|4|Linux mbx01 4.15.0-88-generic #88-Ubuntu SMP Tue Feb 11 20:11:34 UTC 2020 x86_64
+1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.8072.3.2.10
+1.3.6.1.2.1.1.3.0|67|43985546
+1.3.6.1.2.1.1.4.0|4|
+1.3.6.1.2.1.1.5.0|4|
+1.3.6.1.2.1.1.6.0|4|
+1.3.6.1.2.1.25.1.1.0|67|43989477
+1.3.6.1.4.1.8072.1.3.2.2.1.21.15.109.97.105.108.99.111.119.45.112.111.115.116.102.105.120|2|1
+1.3.6.1.4.1.8072.1.3.2.3.1.2.15.109.97.105.108.99.111.119.45.112.111.115.116.102.105.120|4|{"data":{"received":"2","delivered":"4","forwarded":"0","deferred":"0","bounced":"0","rejected":"0","rejectwarnings":"0","held":"0","discarded":"0","bytesreceived":"64182","bytesdelivered":"128364","senders":"2","sendinghostsdomains":"2","recipients":"2","recipienthostsdomains":"2"},"error":"0","errorString":"","version":"1"}