Verified Commit 1fb370b1 authored by Adrian Schollmeyer's avatar Adrian Schollmeyer
Browse files

export-netbox-to-dokuwiki: Add Icinga2 stats export and KeyboardInterrupt handling


Signed-off-by: Adrian Schollmeyer's avatarAdrian 'nex' Schollmeyer <adrian.schollmeyer@fem.tu-ilmenau.de>
parent 79084cae
......@@ -6,9 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
* `export-netbox-to-dokuwiki.py`
* support for retrieving the LDAP password from a command line instead of a hard-coded value
* support for exporting statistics about the NetBox-Wiki-export to an Icinga2 API as check result
### Changed
* Included describing comments in example configuration
* `export-netbox-to-dokuwiki.py` now also creates the overview Wiki page after a KeyboardInterrupt
## 0.1 (2021-04-25)
### Added
......
......@@ -7,6 +7,7 @@ import progressbar
import dokuwiki
import subprocess
import configparser
import requests
from fem_netbox import cli_common
IGNORED_ROLES=[
......@@ -29,12 +30,28 @@ def main():
netbox = cli_common.create_netbox_instance_from_config(config)
if args.wiki_export:
dokuwiki_config = get_dokuwiki_config(config)
exporter = NetboxDokuwikiExporter(netbox, None, dokuwiki_config,
args.progress)
file_destination = None
else:
exporter = NetboxDokuwikiExporter(netbox, args.destination, None,
dokuwiki_config = None
file_destination = args.destination
if args.submit_check_result:
icinga2_config = get_icinga2_config(config)
exporter = NetboxDokuwikiExporter(netbox, file_destination, dokuwiki_config,
args.progress)
exporter.export()
export_stats = exporter.export()
if args.submit_check_result:
res = export_stats.submit_to_icinga2(icinga2_config)
if res.status_code == requests.codes.ok:
return 0
else:
print(f"Icinga2 API request failed with code {res.status_code}")
print(res.text)
return 1
return 0
def create_argument_parser():
......@@ -47,6 +64,8 @@ def create_argument_parser():
help="File output directory")
parser.add_argument("--progress", "-P", action="store_true",
help="Show a progress bar using tqdm")
parser.add_argument("--submit-check-result", "-S", action="store_true",
help="Submit the export result to an Icinga2 API endpoint")
return parser
......@@ -79,10 +98,27 @@ def get_dokuwiki_password_from_pass_command(command):
return password
def get_icinga2_config(config):
api_url = config.get("icinga", "api_url")
api_user = config.get("icinga", "api_user")
api_password = config.get("icinga", "api_password")
icinga2_host = config.get("icinga", "check_host")
icinga2_service = config.get("icinga", "check_service")
return {
"api_url": api_url,
"api_user": api_user,
"api_password": api_password,
"icinga2_host": icinga2_host,
"icinga2_service": icinga2_service
}
class NetboxDokuwikiExporter:
def __init__(self, netbox, file_output, dokuwiki_config, enable_progress):
self.netbox = netbox
self.enable_progress = enable_progress
self.stats = NetboxExporterStats()
if file_output:
self.output = FileExporter(file_output)
......@@ -107,22 +143,28 @@ class NetboxDokuwikiExporter:
self.progress_bar = progressbar.ProgressBar(maxval = max_progress).start()
for device in self.netbox.dcim.devices.all():
if self.is_ignored_role(device.device_role.name):
continue
if not device.name:
continue
self.devices.append(
(self.sanitize_text(device.name),
device.display_name))
self.export_device(device)
if self.enable_progress:
self.progress += 1
self.progress_bar.update(self.progress)
try:
for device in self.netbox.dcim.devices.all():
if self.is_ignored_role(device.device_role.name):
continue
if not device.name:
continue
self.devices.append(
(self.sanitize_text(device.name),
device.display_name))
self.export_device(device)
self.stats.processed_devices_vms += 1
if self.enable_progress:
self.progress += 1
self.progress_bar.update(self.progress)
except KeyboardInterrupt:
print("Interrupted. Finishing work")
self.stats.interrupted = True
self.write_device_list()
return self.stats
def is_ignored_role(self, role_name):
......@@ -160,6 +202,7 @@ class NetboxDokuwikiExporter:
"devices_" + self.sanitize_text(device.name),
wiki_text
)
self.stats.wiki_pages += 1
def write_device_list(self):
......@@ -168,6 +211,7 @@ class NetboxDokuwikiExporter:
text += f" * [[.:devices_{device[0]}|{device[1]}]]\n"
self.output.export("start", text)
self.stats.wiki_pages += 1
def sanitize_text(self, text):
......@@ -192,8 +236,12 @@ class NetboxDokuwikiExporter:
if self.enable_progress:
self.progress += 1
self.progress_bar.update(self.progress)
self.stats.processed_interfaces_ports += 1
return text
def make_front_port_overview(self, front_ports):
text = ""
for port in front_ports:
......@@ -206,6 +254,8 @@ class NetboxDokuwikiExporter:
self.progress += 1
self.progress_bar.update(self.progress)
self.stats.processed_interfaces_ports += 1
return text
......@@ -219,6 +269,8 @@ class NetboxDokuwikiExporter:
self.progress += 1
self.progress_bar.update(self.progress)
self.stats.processed_interfaces_ports += 1
return text
......@@ -240,6 +292,7 @@ class NetboxDokuwikiExporter:
def format_patch(self, interface, cable):
self.stats.processed_patches += 1
if not cable:
return ""
if cable.termination_a == interface:
......@@ -277,6 +330,7 @@ class NetboxDokuwikiExporter:
text = ""
for ip_address in ip_addresses:
text += f"{ip_address.address} \\\\ "
self.stats.processed_ips += 1
return text
......@@ -310,5 +364,56 @@ class DokuWikiExporter:
raise Exception("Failed to upload DokuWiki page!")
class NetboxExporterStats:
def __init__(self):
self.processed_devices_vms = 0
self.wiki_pages = 0
self.processed_interfaces_ports = 0
self.processed_ips = 0
self.processed_patches = 0
self.interrupted = False
def submit_to_icinga2(self, icinga2_config):
icinga2_host = icinga2_config["icinga2_host"]
icinga2_service = icinga2_config["icinga2_service"]
url = icinga2_config["api_url"] + "/v1/actions/process-check-result?" \
+ f"service={icinga2_host}!{icinga2_service}"
data = {
"exit_status": 1 if self.interrupted else 0,
"plugin_output": "interrupted" if self.interrupted else "ok",
"performance_data": [
f"processed_devices_vms={self.processed_devices_vms}",
f"wiki_pages={self.wiki_pages}",
f"processed_interfaces_ports={self.processed_interfaces_ports}",
f"processed_ips={self.processed_ips}",
f"processed_patches={self.processed_patches}"
]
}
if self.interrupted:
data["performance_data"].append("interrupt=1")
res = requests.post(
url,
json=data,
headers={"Accept": "application/json"},
auth=requests.auth.HTTPBasicAuth(
icinga2_config["api_user"],
icinga2_config["api_password"]
)
)
return res
def to_string(self):
return "STATS:\n" + \
f"processed_devices_vms = {self.processed_devices_vms}" \
+ "\n" + f"wiki_pages = {self.wiki_pages}\n" + \
"processed_interfaces_ports = " + \
f"{self.processed_interfaces_ports}" \
+ "\n" + f"processed_ips = {self.processed_ips}" + "\n" + \
f"processed_patches = {self.processed_patches}"
if __name__ == '__main__':
sys.exit(main())
......@@ -17,3 +17,17 @@ username = foo
password = bar
; Or use a password command line:
password_command = pass fem-ldap | head -n1
[icinga]
; API URL base
api_url = https://monitoring.fem-net.de/api
; API username
api_user = netbox
; API password
api_password = somerandomcharacterswhicharethepassword
; Icinga2 Host object to submit check result to
check_host = netbox
; Icinga2 Service object to submit check result to
check_service = dokuwiki-export
; vim: ft=dosini
......@@ -3,3 +3,4 @@ pynetbox>=6.1.2
# for export-netbox-to-dokuwiki
progressbar
dokuwiki
requests
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment