Skip to content

Commit

Permalink
Final (general) approach, still needs some tweaing and validation though
Browse files Browse the repository at this point in the history
  • Loading branch information
TristanPerry committed Apr 1, 2024
1 parent 0050603 commit ef07154
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
# network-device-offline-monitor
A simple way of getting notified by email if a "always online" network device goes offline for any reason
A simple way of getting notified by email if one or more local network devices go offline for any reason.

This will scan any IP ranges specified in config.ini (see `IpRangesToScan`) and checks that any IPs mentioned in `RequiredIps` are online. If they aren't, it will send an email to the specified notification email.

This script is designed for Linux since it uses nmap, however it might be possible to run it on a Windows OS as long as nmap is installed (https://nmap.org/book/inst-windows.html) and available on the `PATH`. I haven't tested this though.

## How To Run

This uses the Mailersend API to send emails (although naturally you can use your own provider or approach by modifying `send_email_notification()`).

The Mailersend API key can only be specified by an environment variable: `MAILERSEND_API_KEY`

So you can run this script by modifying `config.ini` as required, and then running something like:

> MAILERSEND_API_KEY='changeme' python3 main.py
You can also change the logging level by adding a `--loglevel` param, for example by running:

> MAILERSEND_API_KEY='changeme' python3 main.py --loglevel debug
3 changes: 1 addition & 2 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ IpRangesToScan =
192.168.4.0/24
192.168.6.0/24
192.168.7.0/24
OfflineNotificationName = Tristan Perry
OfflineNotificationEmail = [email protected]
RequiredIps =
192.168.7.254
192.168.7.0
192.168.4.27
Blah = Test
51 changes: 43 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import argparse
import logging
import nmap3
import os

from mailersend import emails

logger = logging.getLogger(__name__)

Expand All @@ -11,18 +14,19 @@ def to_list(string: str) -> list:
return list(filter(None, split))


def load_config_values() -> tuple[list, str, list]:
def load_config_values() -> tuple[list, str, str, list]:
config = configparser.ConfigParser()
config.read('config.ini')

to_scan = config['DEFAULT']['IpRangesToScan']
notification_name = config['DEFAULT']['OfflineNotificationName']
notification_email = config['DEFAULT']['OfflineNotificationEmail']
required_ips = config['DEFAULT']['RequiredIps']
logger.debug('Config loaded. (to_scan) %s, (email) %s, (ips) %s', to_scan, notification_email, required_ips)

# TODO validate

return to_list(to_scan), notification_email, to_list(required_ips)
return to_list(to_scan), notification_name, notification_email, to_list(required_ips)


def get_active_local_ips(to_scan: list) -> list:
Expand All @@ -36,7 +40,8 @@ def get_active_local_ips(to_scan: list) -> list:
logger.debug('Results for host range %s', host)
logger.debug(results)

up_ips = {ip: result for ip, result in results.items() if 'state' in result and result['state']['state'] == 'up'}
up_ips = {ip: result for ip, result in results.items() if
'state' in result and result['state']['state'] == 'up'}

up_ips_list = list(up_ips.keys())
logger.debug('%s IPs are UP for host range %s', up_ips_list, host)
Expand All @@ -45,8 +50,40 @@ def get_active_local_ips(to_scan: list) -> list:
return active_ips_in_range


def send_email_notification(error_ips: list, notification_email: str) -> None:
def send_email_notification(error_ips: list, notification_name: str, notification_email: str) -> None:
logger.error('IP(s) %s are offline, sending email to %s', error_ips, notification_email)
mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY'))

mail_body = {}

mail_from = {
"name": "Offline Notifier",
"email": "[email protected]",
}

recipients = [
{
"name": notification_name,
"email": notification_email,
}
]

reply_to = [
{
"name": "No Reply",
"email": "[email protected]",
}
]

mailer.set_mail_from(mail_from, mail_body)
mailer.set_mail_to(recipients, mail_body)
mailer.set_reply_to(reply_to, mail_body)
mailer.set_subject("Network Device(s) Offline", mail_body)
mailer.set_plaintext_content("Warning: the following required IP addresses are offline: " + ', '.join(error_ips),
mail_body)

response = mailer.send(mail_body)
logger.debug("Email response: %s", response)


if __name__ == '__main__':
Expand All @@ -59,14 +96,12 @@ def send_email_notification(error_ips: list, notification_email: str) -> None:
logging.basicConfig(level=args.loglevel.upper())
logging.info('Logging setup')

ip_ranges_to_scan, email, required_ips = load_config_values()
ip_ranges_to_scan, name, email, required_ips = load_config_values()
all_active_ips = get_active_local_ips(ip_ranges_to_scan)
logger.info('All active IPs: %s', all_active_ips)

offline_ips = list(set(required_ips) - set(all_active_ips))
logger.info('Offline IPs %s', offline_ips)

if offline_ips:
send_email_notification(offline_ips)

# See PyCharm help at https://www.jetbrains.com/help/pycharm/
send_email_notification(offline_ips, name, email)
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
configparser~=6.0.1
python3-nmap~=1.6.0
python3-nmap~=1.6.0
mailersend~=0.5.6

0 comments on commit ef07154

Please sign in to comment.