Skip to content

Commit

Permalink
Merge pull request #13 from votdev/use_global_config_file
Browse files Browse the repository at this point in the history
Add support for global configuration file
  • Loading branch information
votdev authored May 14, 2019
2 parents 124be63 + 4464293 commit c0318b0
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 40 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ If you want to send a test SNMP trap, then simply execute the following command.
$ ./prometheus-webhook-snmp test

# Command line parameters
Command line parameters have precedence over global configuration settings.

## Global

Expand Down Expand Up @@ -94,6 +95,19 @@ scrape_configs:
- targets: ['localhost:9099']
```
# Global configuration file
The Prometheus Alertmanager receiver can be configured via configuration file, too. The file ``/etc/prometheus-webhook-snmp.conf`` is written in YAML format. Parameters in this file have precedence over default configuration settings. Please replace hyphens in parameter names with underscores.
Example configuration:
```yaml
debug: True
snmp_retries: 1
snmp_community: private
host: promalertmgr.foo.com
port: 9101
```
# SNMP schema
## Traps
Expand Down
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
prometheus-webhook-snmp (1.1-1) stable; urgency=low

* Add support for global configuration file.

-- Volker Theile <[email protected]> Mon, 13 May 2019 16:01:19 +0200

prometheus-webhook-snmp (1.0-1) stable; urgency=low

* Initial Debian packaging.
Expand Down
3 changes: 2 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Depends: ${misc:Depends},
python3-click,
python3-cherrypy3,
python3-dateutil,
python3-pysnmp4
python3-pysnmp4,
python3-yaml
Priority: optional
Description: Prometheus Alertmanager receiver for SNMP traps
prometheus-webhook-snmp is a Prometheus Alertmanager receiver that
Expand Down
1 change: 1 addition & 0 deletions debian/prometheus-webhook-snmp.default
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROMETHEUS_WEBHOOK_SNMP_OPTIONS=""
28 changes: 5 additions & 23 deletions prometheus-webhook-snmp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import click
from prometheus_webhook_snmp import utils


__version__ = "1.0"
__version__ = "1.1"


pass_context = click.make_pass_decorator(utils.Context, ensure=True)

Expand All @@ -19,42 +20,27 @@ pass_context = click.make_pass_decorator(utils.Context, ensure=True)
is_flag=True,
help='Enable debug output.')
@click.option('--snmp-host',
default='localhost',
show_default=True,
help='The host (IP or FQDN) of the SNMP trap receiver.')
@click.option('--snmp-port',
default=162,
show_default=True,
help='The port of the SNMP trap receiver.')
@click.option('--snmp-community',
default='public',
show_default=True,
help='The SNMP community string.')
@click.option('--snmp-retries',
default=5,
show_default=True,
help='Maximum number of request retries.')
@click.option('--snmp-timeout',
default=1,
show_default=True,
help='Response timeout in seconds.')
@click.option('--alert-oid-label',
default='oid',
show_default=True,
help='The label where to find the OID.')
@click.option('--trap-oid-prefix',
default='1.3.6.1.4.1.50495.15',
show_default=True,
help='The OID prefix for trap variable bindings.')
@click.option('--trap-default-oid',
default='1.3.6.1.4.1.50495.15.1.2.1',
show_default=True,
help='The trap OID if none is found in the Prometheus alert labels.')
@click.version_option(__version__, message="%(version)s")
@pass_context
def cli(ctx, debug, snmp_host, snmp_port, snmp_community, snmp_retries,
snmp_timeout, alert_oid_label, trap_oid_prefix, trap_default_oid):
ctx.config['debug'] = debug
ctx.config.load(click.get_current_context().info_name)
ctx.config['debug'] = True if debug else None
ctx.config['snmp_host'] = snmp_host
ctx.config['snmp_port'] = snmp_port
ctx.config['snmp_community'] = snmp_community
Expand All @@ -72,12 +58,8 @@ def cli(ctx, debug, snmp_host, snmp_port, snmp_community, snmp_retries,

@cli.command(name='run', help='Start the HTTP server.')
@click.option('--host',
default='0.0.0.0',
show_default=True,
help='Host to use.')
@click.option('--port',
default=9099,
show_default=True,
help='Port to listen for Prometheus Alertmanager notifications.')
@click.option('--metrics',
is_flag=True,
Expand All @@ -86,7 +68,7 @@ def cli(ctx, debug, snmp_host, snmp_port, snmp_community, snmp_retries,
def run(ctx, host, port, metrics):
ctx.config['host'] = host
ctx.config['port'] = port
ctx.config['metrics'] = metrics
ctx.config['metrics'] = True if metrics else None
utils.run_http_server(ctx)
sys.exit(0)

Expand Down
3 changes: 2 additions & 1 deletion prometheus-webhook-snmp.service
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Description=Prometheus Alertmanager receiver for SNMP traps
After=network.target

[Service]
ExecStart=/usr/bin/prometheus-webhook-snmp run
EnvironmentFile=-/etc/default/prometheus-webhook-snmp
ExecStart=/usr/bin/prometheus-webhook-snmp $PROMETHEUS_WEBHOOK_SNMP_OPTIONS run
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure

Expand Down
6 changes: 4 additions & 2 deletions prometheus-webhook-snmp.spec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# Please submit bugfixes or comments via http://bugs.opensuse.org/

Name: prometheus-webhook-snmp
Version: 1.0
Version: 1.1
Release: 0
Summary: Prometheus Alertmanager receiver for SNMP traps
License: GPL-3.0
Expand All @@ -34,11 +34,13 @@ Requires: python3-prometheus-client
Requires: python3-click
%if 0%{?suse_version}
Requires: python3-CherryPy
Requires: python3-PyYAML
%else
Requires: python3-cherrypy
Requires: python3-yaml
%endif
Requires: python3-dateutil
Requires: python3-pysnmp
Requires: python3-pysnmp >= 4.3.2

%description
prometheus-webhook-snmp is a Prometheus Alertmanager receiver that
Expand Down
76 changes: 68 additions & 8 deletions prometheus_webhook_snmp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cherrypy
import dateutil.parser
import prometheus_client
import yaml

from pysnmp import hlapi

Expand Down Expand Up @@ -159,6 +160,72 @@ def run_http_server(ctx):
cherrypy.quickstart(Root(ctx), config=get_http_server_config())


class Config(dict):
def __init__(self):
super().__init__()
self.reset()

@staticmethod
def defaults():
"""
Get the default configuration values.
:return: Returns a dictionary containing the default values.
:rtype: dict
"""
return {
'debug': False,
'snmp_host': 'localhost',
'snmp_port': 162,
'snmp_community': 'public',
'snmp_retries': 5,
'snmp_timeout': 1,
'alert_oid_label': 'oid',
'trap_oid_prefix': '1.3.6.1.4.1.50495.15',
'trap_default_oid': '1.3.6.1.4.1.50495.15.1.2.1',
'host': '0.0.0.0',
'port': 9099,
'metrics': False
}

def reset(self, name=None):
"""
Reset to default values. If a name is specified, only the named
configuration setting is reset to default.
:param name: The name of the configuration setting. Defaults to 'None'.
:type name: str
"""
if name is None:
self.clear()
self.update(Config.defaults())
else:
self[name] = Config.defaults()[name]

def load(self, prog_name):
"""
Load a configuration file from disk.
:param prog_name: The name of the program.
:type prog_name: str
"""
file_name = '/etc/{}.conf'.format(prog_name)
try:
with open(file_name, 'r') as stream:
config = yaml.safe_load(stream)
self.update(config)
except (IOError, FileNotFoundError):
pass

def __setitem__(self, key, value):
"""
Set self[key] to value. Ignore 'None' values.
:param key: The name of the key.
:type key: str
:param value: The value of the key.
:type value: bool|int|str
"""
if value is not None:
super().__setitem__(key, value)


class Telemetry:
def __init__(self):
self.metrics = {
Expand All @@ -183,14 +250,7 @@ def generate(self):

class Context:
def __init__(self):
self.config = {
key: None
for key in [
'host', 'port', 'metrics', 'snmp_host', 'snmp_port',
'snmp_community', 'snmp_retries', 'snmp_timeout',
'alert_oid_label', 'trap_default_oid', 'trap_oid_prefix'
]
}
self.config = Config()
self.telemetry = Telemetry()


Expand Down
11 changes: 7 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
CherryPy==18.1.1
Click==7.0
pysnmp==4.4.9
python-dateutil==2.8.0
pytest==4.4.0
pylint==2.3.1
PyYAML==5.1
mock==3.0.5
prometheus_client==0.6.0
pyfakefs==3.5.8
pylint==2.3.1
pysnmp>=4.3.2
pytest==4.4.0
python-dateutil==2.8.0
32 changes: 31 additions & 1 deletion tests/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import unittest
import mock

from prometheus_webhook_snmp.utils import parse_notification
from pyfakefs import fake_filesystem

from prometheus_webhook_snmp.utils import parse_notification, Config

NOTIFICATION_FIRING = {
'receiver': 'test-01',
Expand Down Expand Up @@ -110,3 +113,30 @@ def test_parse_notification_no_oid(self):
self.assertEqual(trap_data['labels'], {'foo': 'abc', 'bar': 123})
self.assertEqual(trap_data['timestamp'], 1554110387)
self.assertIsInstance(trap_data['rawdata'], dict)


class ConfigTestCase(unittest.TestCase):
fs = fake_filesystem.FakeFilesystem()
f_open = fake_filesystem.FakeFileOpen(fs)

def test_defaults(self):
self.assertIsInstance(Config.defaults(), dict)

def test_reset(self):
config = Config()
config['snmp_community'] = 'private'
config.reset('snmp_community')
self.assertEqual(config['snmp_community'], Config.defaults()['snmp_community'])

def test_reset_all(self):
config = Config()
config['foo'] = 'bar'
config.reset()
self.assertDictEqual(config, Config.defaults())

@mock.patch('builtins.open', new=f_open)
def test_load(self):
self.fs.create_file('/etc/abc.conf', contents='''foo: bar\n''')
config = Config()
config.load('abc')
self.assertEqual(config['foo'], 'bar')

0 comments on commit c0318b0

Please sign in to comment.