Compare commits
10 commits
cbf5132b69
...
4f854bca8b
Author | SHA1 | Date | |
---|---|---|---|
4f854bca8b | |||
df5296af42 | |||
0cd101954c | |||
7cdc46954e | |||
ca3adb00ce | |||
7fa647c953 | |||
2b5a263d48 | |||
17f0948aeb | |||
a7c9f1455e | |||
a0507d546a |
9 changed files with 154 additions and 45 deletions
|
@ -11,6 +11,8 @@ repos:
|
|||
args: ['--remove']
|
||||
- id: requirements-txt-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: check-json
|
||||
- id: check-yaml
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: master
|
||||
hooks:
|
||||
|
|
5
Dockerfile
Normal file
5
Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM python:3.8-slim
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git \
|
||||
&& apt-get autoclean \
|
||||
&& pip install pre-commit
|
59
README.md
59
README.md
|
@ -15,19 +15,25 @@ Clone the repository:
|
|||
git clone https://github.com/jouir/notify-by-telegram.git /opt/notify-by-telegram
|
||||
```
|
||||
|
||||
Install dependencies using the package manager:
|
||||
Install dependencies using pip:
|
||||
```
|
||||
sudo apt install python3-jinja2 python3-requests
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Or via the package manager:
|
||||
```
|
||||
sudo apt install python3-jinja2 python3-requests python3-jsonschema
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy and update the configuration file example:
|
||||
```
|
||||
cp -p config.json.example telegram.json
|
||||
cp -p config.example.json telegram.json
|
||||
vim telegram.json
|
||||
sudo mv telegram.json /etc/nagios4/telegram.json
|
||||
sudo chmod 640 root:nagios /etc/nagios4/telegram.json
|
||||
sudo chown root:nagios /etc/nagios4/telegram.json
|
||||
sudo chmod 640 /etc/nagios4/telegram.json
|
||||
```
|
||||
|
||||
Ensure Nagios reads the configuration file:
|
||||
|
@ -40,7 +46,16 @@ Then reload service:
|
|||
systemctl reload nagios4
|
||||
```
|
||||
|
||||
## Logs
|
||||
## Configuration file
|
||||
|
||||
Format used is JSON with the following keys:
|
||||
|
||||
* `chat_id`: where to send message on Telegram
|
||||
* `auth_key`: key used to authenticate on Telegram
|
||||
* `host_template` (optional): path to Markdown template file used for sending host notifications
|
||||
* `service_template` (optional): path to Markdown template file used for sending service notifications
|
||||
|
||||
## Logging
|
||||
|
||||
Errors logs can be set with the `--logfile` argument.
|
||||
|
||||
|
@ -70,3 +85,37 @@ They can be overriden in the configuration file:
|
|||
```
|
||||
|
||||
Both options are optional.
|
||||
|
||||
### Host variables
|
||||
|
||||
Variables replaced in the host template:
|
||||
* `notification_type` (= `$NOTIFICATIONTYPE$`)
|
||||
* `host_name` (= `$HOSTNAME$`)
|
||||
* `host_state` (= `$HOSTSTATE$`)
|
||||
* `host_address` (= `$HOSTADDRESS$`)
|
||||
* `host_output` (= `$HOSTOUTPUT$`)
|
||||
* `long_date_time` (= `$LONGDATETIME$`)
|
||||
|
||||
### Service variables
|
||||
|
||||
Variables replaced in the service template:
|
||||
* `notification_type` (= `$NOTIFICATIONTYPE$`)
|
||||
* `service_desc` (= `$SERVICEDESC$`)
|
||||
* `host_alias` (= `$HOSTALIAS$`)
|
||||
* `host_address` (= `$HOSTADDRESS$`)
|
||||
* `service_state` (= `$SERVICESTATE$`)
|
||||
* `long_date_time` (= `$LONGDATETIME$`)
|
||||
* `service_output` (= `$SERVICEOUTPUT$`)
|
||||
|
||||
## How to contribute
|
||||
|
||||
Contributions are welcomed! Feel free to update the code and create a pull-request.
|
||||
|
||||
Be sure to lint the code before:
|
||||
```
|
||||
docker build -t pre-commit .
|
||||
docker run -it -v $(pwd):/mnt/ --rm pre-commit bash
|
||||
# cd /mnt/
|
||||
# pre-commit run --all-files
|
||||
# exit
|
||||
```
|
||||
|
|
4
TODO.txt
4
TODO.txt
|
@ -1,4 +0,0 @@
|
|||
Factorize the template rendering functions
|
||||
Use JSON schema to validate configuration file
|
||||
Add pre-commit Dockerfile and a doc to easily lint the code
|
||||
Add requirements.txt file
|
21
config.schema.json
Normal file
21
config.schema.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chat_id": {
|
||||
"type": "number"
|
||||
},
|
||||
"auth_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"host_template": {
|
||||
"type": "string"
|
||||
},
|
||||
"service_template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chat_id",
|
||||
"auth_key"
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
define command {
|
||||
command_name notify-host-by-telegram
|
||||
command_line /opt/notify-by-telegram/notify-by-telegram.py -c /etc/nagios4/telegram.json --logfile /var/log/nagios4/telegram.log host --notification-type "$NOTIFICATIONTYPE$" --service-desc "$SERVICEDESC$" --host-name "$HOSTNAME$" --host-state "$HOSTSTATE$" --host-address "$HOSTADDRESS$" --host-output "$HOSTOUTPUT$" --long-date-time "$LONGDATETIME$"
|
||||
command_line /opt/notify-by-telegram/notify-by-telegram.py -c /etc/nagios4/telegram.json --logfile /var/log/nagios4/telegram.log host --notification-type "$NOTIFICATIONTYPE$" --host-name "$HOSTNAME$" --host-state "$HOSTSTATE$" --host-address "$HOSTADDRESS$" --host-output "$HOSTOUTPUT$" --long-date-time "$LONGDATETIME$"
|
||||
}
|
||||
|
||||
define command {
|
||||
|
|
|
@ -6,8 +6,10 @@ import os
|
|||
|
||||
import requests
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from jsonschema import validate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
absolute_path = os.path.split(os.path.abspath(__file__))[0]
|
||||
|
||||
|
||||
class InvalidConfigException(Exception):
|
||||
|
@ -28,7 +30,6 @@ def parse_arguments():
|
|||
# host notifications
|
||||
host_parser = subparsers.add_parser('host')
|
||||
host_parser.add_argument('--notification-type', help='nagios $NOTIFICATIONTYPE$', required=True)
|
||||
host_parser.add_argument('--service-desc', help='nagios $SERVICEDESC$', required=True)
|
||||
host_parser.add_argument('--host-name', help='nagios $HOSTNAME$', required=True)
|
||||
host_parser.add_argument('--host-state', help='nagios $HOSTSTATE$', required=True)
|
||||
host_parser.add_argument('--host-address', help='nagios $HOSTADDRESS$', required=True)
|
||||
|
@ -60,9 +61,9 @@ def read_config(filename=None):
|
|||
def validate_config(config):
|
||||
if config is None:
|
||||
raise InvalidConfigException('config is not a dict')
|
||||
for key in ['chat_id', 'auth_key']:
|
||||
if key not in config:
|
||||
raise InvalidConfigException(f'missing "{key}" key in config')
|
||||
with open(os.path.join(absolute_path, 'config.schema.json'), 'r') as fd:
|
||||
schema = json.loads(fd.read())
|
||||
validate(instance=config, schema=schema)
|
||||
|
||||
|
||||
def setup_logging(args):
|
||||
|
@ -71,48 +72,56 @@ def setup_logging(args):
|
|||
|
||||
|
||||
def markdown_escape(text):
|
||||
for special_char in ['\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '#', '+', '-', '.', '!']:
|
||||
for special_char in ['\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '#', '+', '-', '.', '!', '=']:
|
||||
text = text.replace(special_char, fr'\{special_char}')
|
||||
return text
|
||||
|
||||
|
||||
def generate_host_payload(chat_id, args, template_file_name=None):
|
||||
def generate_payload(chat_id, message_type, message_variables, template_file_name=None):
|
||||
payload = {'chat_id': chat_id, 'parse_mode': 'MarkdownV2'}
|
||||
|
||||
# define jinja template name
|
||||
if not template_file_name:
|
||||
absolute_path = os.path.split(os.path.abspath(__file__))[0]
|
||||
template_file_name = os.path.join(absolute_path, 'templates', 'host.md.j2')
|
||||
template_name = 'host.md.j2' if message_type == 'host' else 'service.md.j2'
|
||||
template_file_name = os.path.join(absolute_path, 'templates', template_name)
|
||||
template_path = os.path.dirname(os.path.abspath(template_file_name))
|
||||
template_name = os.path.basename(os.path.abspath(template_file_name))
|
||||
|
||||
# create a jinja file template
|
||||
loader = FileSystemLoader(template_path)
|
||||
env = Environment(loader=loader)
|
||||
template = env.get_template(template_name)
|
||||
text = template.render(notification_type=markdown_escape(args.notification_type),
|
||||
host_name=markdown_escape(args.host_name), host_state=markdown_escape(args.host_state),
|
||||
host_address=markdown_escape(args.host_address),
|
||||
host_output=markdown_escape(args.host_output),
|
||||
long_date_time=markdown_escape(args.long_date_time))
|
||||
|
||||
# escape message variables
|
||||
template_variables = {}
|
||||
for key, value in message_variables.items():
|
||||
template_variables[key] = markdown_escape(value)
|
||||
|
||||
# render template and update payload
|
||||
text = template.render(**template_variables)
|
||||
payload['text'] = text
|
||||
return payload
|
||||
|
||||
|
||||
def generate_service_payload(chat_id, args, template_file_name=None):
|
||||
payload = {'chat_id': chat_id, 'parse_mode': 'MarkdownV2'}
|
||||
if not template_file_name:
|
||||
absolute_path = os.path.split(os.path.abspath(__file__))[0]
|
||||
template_file_name = os.path.join(absolute_path, 'templates', 'service.md.j2')
|
||||
template_path = os.path.dirname(os.path.abspath(template_file_name))
|
||||
template_name = os.path.basename(os.path.abspath(template_file_name))
|
||||
loader = FileSystemLoader(template_path)
|
||||
env = Environment(loader=loader)
|
||||
template = env.get_template(template_name)
|
||||
text = template.render(notification_type=markdown_escape(args.notification_type),
|
||||
service_desc=markdown_escape(args.service_desc), host_alias=markdown_escape(args.host_alias),
|
||||
host_address=markdown_escape(args.host_address),
|
||||
service_state=markdown_escape(args.service_state),
|
||||
long_date_time=markdown_escape(args.long_date_time),
|
||||
service_output=markdown_escape(args.service_output))
|
||||
payload['text'] = text
|
||||
return payload
|
||||
def generate_host_payload(chat_id, notification_type, host_name, host_state, host_address, host_output, long_date_time,
|
||||
template_file_name=None):
|
||||
message_variables = {
|
||||
'notification_type': notification_type, 'host_name': host_name, 'host_state': host_state,
|
||||
'host_address': host_address, 'host_output': host_output, 'long_date_time': long_date_time
|
||||
}
|
||||
return generate_payload(chat_id=chat_id, message_type='host', message_variables=message_variables,
|
||||
template_file_name=template_file_name)
|
||||
|
||||
|
||||
def generate_service_payload(chat_id, notification_type, service_desc, host_alias, host_address, service_state,
|
||||
long_date_time, service_output, template_file_name=None):
|
||||
message_variables = {
|
||||
'notification_type': notification_type, 'service_desc': service_desc, 'host_alias': host_alias,
|
||||
'host_address': host_address, 'service_state': service_state, 'long_date_time': long_date_time,
|
||||
'service_output': service_output
|
||||
}
|
||||
return generate_payload(chat_id=chat_id, message_type='service', message_variables=message_variables,
|
||||
template_file_name=template_file_name)
|
||||
|
||||
|
||||
def send_message(auth_key, payload):
|
||||
|
@ -134,17 +143,23 @@ def main():
|
|||
|
||||
logger.info('generating payload')
|
||||
if args.command == 'host':
|
||||
payload = generate_host_payload(chat_id=config['chat_id'], args=args,
|
||||
payload = generate_host_payload(chat_id=config['chat_id'], notification_type=args.notification_type,
|
||||
host_name=args.host_name, host_state=args.host_state,
|
||||
host_address=args.host_address, host_output=args.host_output,
|
||||
long_date_time=args.long_date_time,
|
||||
template_file_name=config.get('host_template'))
|
||||
elif args.command == 'service':
|
||||
payload = generate_service_payload(chat_id=config['chat_id'], args=args,
|
||||
template_file_name=config.get('service_template'))
|
||||
payload = generate_service_payload(chat_id=config['chat_id'], notification_type=args.notification_type,
|
||||
service_desc=args.service_desc, host_alias=args.host_alias,
|
||||
host_address=args.host_address, service_state=args.service_state,
|
||||
long_date_time=args.long_date_time, service_output=args.service_output,
|
||||
template_file_name=config.get('service_template'))
|
||||
else:
|
||||
raise NotImplementedError(f'command {args.command} not supported')
|
||||
|
||||
logger.info('sending message to telegram api')
|
||||
send_message(auth_key=config['auth_key'], payload=payload)
|
||||
except Exception as err:
|
||||
except Exception:
|
||||
logger.exception('cannot execute program')
|
||||
|
||||
|
||||
|
|
21
requirements.txt
Normal file
21
requirements.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
appdirs==1.4.4
|
||||
attrs==20.3.0
|
||||
certifi==2020.11.8
|
||||
cfgv==3.2.0
|
||||
chardet==3.0.4
|
||||
distlib==0.3.1
|
||||
filelock==3.0.12
|
||||
identify==1.5.10
|
||||
idna==2.10
|
||||
Jinja2==2.11.2
|
||||
jsonschema==3.2.0
|
||||
MarkupSafe==1.1.1
|
||||
nodeenv==1.5.0
|
||||
pre-commit==2.9.2
|
||||
pyrsistent==0.17.3
|
||||
PyYAML==5.3.1
|
||||
requests==2.25.0
|
||||
six==1.15.0
|
||||
toml==0.10.2
|
||||
urllib3==1.26.2
|
||||
virtualenv==20.2.1
|
Loading…
Reference in a new issue