NagiosToZabbixConverter/nagiosToZabbixConverter.sh

272 lines
11 KiB
Bash
Raw Permalink Normal View History

2025-07-19 23:01:45 +00:00
#!/bin/bash
# --- IMPORTANT: Configure your Nagios config path here ---
NAGIOS_CFG_PATH="/etc/nagios/nagios.cfg"
# This path is now updated based on your input.
# Output files - Bash will manage these for Python
ZABBIX_HOSTGROUPS_OUTPUT="zabbix_hostgroups.txt"
ZABBIX_HOSTS_OUTPUT="zabbix_hosts.txt"
ZABBIX_COMMANDS_OUTPUT="zabbix_commands.txt" # This will now contain JSON
ZABBIX_SERVICES_RAW_OUTPUT="zabbix_services_raw.txt"
ZABBIX_INCLUDED_FILES_LOG="nagios_parsed_files.log"
echo "--- Nagios to Zabbix Export Script (Bash wrapper for Python) ---"
echo "Starting parsing from: $NAGIOS_CFG_PATH"
echo ""
# Check if Python3 is available
if ! command -v python3 &> /dev/null
then
echo "Error: python3 could not be found. Please install it."
exit 1
fi
# Check if the Nagios config file exists
if [[ ! -f "$NAGIOS_CFG_PATH" ]]; then
echo "Error: Main Nagios config file not found at $NAGIOS_CFG_PATH"
echo "Please update NAGIOS_CFG_PATH in the script if it's incorrect."
exit 1
fi
# Run the embedded Python script
python3 - "$NAGIOS_CFG_PATH" <<EOF_PYTHON
import os
import re
import json
import sys
# Get NAGIOS_CFG_PATH from command line argument passed by bash
NAGIOS_CFG_PATH = sys.argv[1]
# Output file paths (these are set in the bash script, but Python will handle writing)
ZABBIX_HOSTGROUPS_OUTPUT = os.environ.get('ZABBIX_HOSTGROUPS_OUTPUT', 'zabbix_hostgroups.txt')
ZABBIX_HOSTS_OUTPUT = os.environ.get('ZABBIX_HOSTS_OUTPUT', 'zabbix_hosts.txt')
ZABBIX_COMMANDS_OUTPUT = os.environ.get('ZABBIX_COMMANDS_OUTPUT', 'zabbix_commands.txt')
ZABBIX_SERVICES_RAW_OUTPUT = os.environ.get('ZABBIX_SERVICES_RAW_OUTPUT', 'zabbix_services_raw.txt')
ZABBIX_INCLUDED_FILES_LOG = os.environ.get('ZABBIX_INCLUDED_FILES_LOG', 'nagios_parsed_files.log')
# Clear existing files before starting Python execution
with open(ZABBIX_HOSTGROUPS_OUTPUT, 'w') as f: pass
with open(ZABBIX_HOSTS_OUTPUT, 'w') as f: pass
with open(ZABBIX_COMMANDS_OUTPUT, 'w') as f: pass # Clear commands file
with open(ZABBIX_SERVICES_RAW_OUTPUT, 'w') as f: pass
with open(ZABBIX_INCLUDED_FILES_LOG, 'w') as f: pass
class NagiosConfigParser:
def __init__(self, nagios_cfg_path):
self.nagios_cfg_path = nagios_cfg_path
self.parsed_data = {
"hosts": {},
"hostgroups": {},
"services": {},
"commands": {},
"contacts": {},
"contactgroups": {},
"timeperiods": {}
}
self.included_files = set()
self.base_dir = os.path.dirname(nagios_cfg_path) if os.path.isfile(nagios_cfg_path) else '/'
def _read_config_file(self, file_path):
"""Reads a configuration file and returns its content."""
try:
with open(file_path, 'r') as f:
return f.read()
except FileNotFoundError:
print(f"Warning: Configuration file not found: {file_path}", file=sys.stderr)
return ""
except Exception as e:
print(f"Error reading {file_path}: {e}", file=sys.stderr)
return ""
def _parse_object(self, content, object_type):
"""Parses a specific object type (e.g., define host { ... })."""
pattern = re.compile(rf'define\s+{object_type}\s+{{\s*(.*?)\s*}}', re.DOTALL)
matches = pattern.findall(content)
objects = {}
for match in matches:
attributes = {}
lines = match.strip().split('\n')
object_name = None
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
parts = line.split(' ', 1)
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip()
attributes[key] = value
if f"{object_type}_name" in key:
object_name = value
if object_name:
objects[object_name] = attributes
else:
command_name = attributes.get('command_name')
if command_name:
objects[command_name] = attributes
return objects
def _find_and_parse_includes(self, content, current_file_path):
"""Finds 'cfg_file' and 'cfg_dir' directives and parses them."""
cfg_file_pattern = re.compile(r'^\s*cfg_file=(.*)$', re.MULTILINE)
cfg_dir_pattern = re.compile(r'^\s*cfg_dir=(.*)$', re.MULTILINE)
current_dir = os.path.dirname(current_file_path)
for match in cfg_file_pattern.finditer(content):
file_path = match.group(1).strip()
full_path = os.path.join(current_dir, file_path) if not os.path.isabs(file_path) else file_path
if full_path not in self.included_files:
self.included_files.add(full_path)
self.parse_config_file(full_path)
for match in cfg_dir_pattern.finditer(content):
dir_path = match.group(1).strip()
full_dir_path = os.path.join(current_dir, dir_path) if not os.path.isabs(dir_path) else dir_path
if os.path.isdir(full_dir_path):
for root, _, files in os.walk(full_dir_path):
for file in files:
if file.endswith('.cfg'):
full_path = os.path.join(root, file)
if full_path not in self.included_files:
self.included_files.add(full_path)
self.parse_config_file(full_path)
else:
print(f"Warning: Configuration directory not found: {full_dir_path}", file=sys.stderr)
def parse_config_file(self, file_path):
"""Recursively parses a Nagios configuration file."""
if file_path in self.included_files and file_path != self.nagios_cfg_path:
return
self.included_files.add(file_path)
print(f" Parsing: {file_path}") # Print progress
with open(ZABBIX_INCLUDED_FILES_LOG, 'a') as f: # Log parsed files
f.write(f"{file_path}\n")
content = self._read_config_file(file_path)
if not content:
return
self.parsed_data["hosts"].update(self._parse_object(content, "host"))
self.parsed_data["hostgroups"].update(self._parse_object(content, "hostgroup"))
self.parsed_data["services"].update(self._parse_object(content, "service"))
self.parsed_data["commands"].update(self._parse_object(content, "command"))
self.parsed_data["contacts"].update(self._parse_object(content, "contact"))
self.parsed_data["contactgroups"].update(self._parse_object(content, "contactgroup"))
self.parsed_data["timeperiods"].update(self._parse_object(content, "timeperiod"))
self._find_and_parse_includes(content, file_path)
def get_zabbix_export_data(self):
"""Formats the parsed data into a structure suitable for Zabbix import."""
zabbix_data = {
"templates": {},
"hosts": [],
"hostgroups": [],
"actions": [],
"commands_as_items": {}
}
# Zabbix Hostgroups
for hg_name, hg_attrs in self.parsed_data["hostgroups"].items():
zabbix_data["hostgroups"].append({
"name": hg_name,
"alias": hg_attrs.get("alias", hg_name),
"members": [m.strip() for m in hg_attrs.get("members", "").split(',')] if hg_attrs.get("members") else []
})
# Zabbix Hosts
for host_name, host_attrs in self.parsed_data["hosts"].items():
interfaces = []
if host_attrs.get("address"):
interfaces.append({
"type": 1, # Agent interface
"main": 1,
"useip": 1,
"ip": host_attrs["address"],
"dns": "",
"port": "10050"
})
host_groups = []
if host_attrs.get("hostgroups"):
for group_name in host_attrs["hostgroups"].split(','):
host_groups.append({"name": group_name.strip()})
zabbix_data["hosts"].append({
"host": host_name,
"name": host_attrs.get("alias", host_name),
"description": host_attrs.get("notes", ""),
"interfaces": interfaces,
"groups": host_groups,
"templates": []
})
# Nagios Commands can become Zabbix items
for cmd_name, cmd_attrs in self.parsed_data["commands"].items():
zabbix_data["commands_as_items"][cmd_name] = {
"command_line": cmd_attrs.get("command_line", ""),
"description": f"Nagios Command: {cmd_name}",
"raw_attributes": cmd_attrs # Include all raw attributes for more detailed mapping
}
zabbix_data["services_raw"] = self.parsed_data["services"]
zabbix_data["contacts_raw"] = self.parsed_data["contacts"]
zabbix_data["contactgroups_raw"] = self.parsed_data["contactgroups"]
return zabbix_data
# --- Main execution logic for Python ---
parser = NagiosConfigParser(NAGIOS_CFG_PATH)
parser.parse_config_file(NAGIOS_CFG_PATH)
export_data = parser.get_zabbix_export_data()
# Write results to the designated output files
with open(ZABBIX_HOSTGROUPS_OUTPUT, 'a') as f:
f.write("--- Hostgroups ---\n")
for hg in export_data["hostgroups"]:
f.write(f" Name: {hg['name']}, Alias: {hg['alias']}\n")
f.write(f" Members: {', '.join(hg['members'])}\n")
with open(ZABBIX_HOSTS_OUTPUT, 'a') as f:
f.write("--- Hosts ---\n")
for host in export_data["hosts"]:
f.write(f" Host: {host['host']}, Name: {host['name']}\n")
f.write(f" Description: {host['description']}\n")
f.write(f" Interfaces: {json.dumps(host['interfaces'])}\n")
f.write(f" Groups: {[g['name'] for g in host['groups']]}\n")
# Explicitly write commands data as JSON to its file
with open(ZABBIX_COMMANDS_OUTPUT, 'a') as f:
f.write("--- Commands (JSON Data) ---\n")
# Dump the entire dictionary of commands as a single JSON object for easy parsing
json.dump(export_data["commands_as_items"], f, indent=2)
with open(ZABBIX_SERVICES_RAW_OUTPUT, 'a') as f:
f.write("--- Services (Raw - requires further Zabbix mapping) ---\n")
for service_name, service_info in export_data["services_raw"].items():
f.write(f" Service: {service_name}\n")
f.write(f" Host Name: {service_info.get('host_name', 'N/A')}\n")
f.write(f" Check Command: {service_info.get('check_command', 'N/A')}\n")
f.write(f" All attributes: {json.dumps(service_info, indent=2)}\n")
EOF_PYTHON
echo ""
echo "--- Export complete. Check the following files: ---"
echo "- $ZABBIX_HOSTGROUPS_OUTPUT"
echo "- $ZABBIX_HOSTS_OUTPUT"
echo "- $ZABBIX_COMMANDS_OUTPUT" # Explicitly listed now
echo "- $ZABBIX_SERVICES_RAW_OUTPUT (for manual mapping to Zabbix Items/Triggers)"
echo "- $ZABBIX_INCLUDED_FILES_LOG (list of all parsed Nagios config files)"
echo ""
echo "There you have it, Kuro. Commands now given their proper place in the export. A well-ordered system, even when it's just a collection of text files. My work here is never truly done, is it?"