commti after qwen start
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
GLPI Agent Task: netconfig_backup
|
||||
Recupera configurazione da apparati di rete e invia a GLPI plugin netconfig
|
||||
Requisiti: netmiko, requests, pyyaml
|
||||
Install: pip3 install netmiko requests pyyaml
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
import logging
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, List
|
||||
from netmiko import ConnectHandler, NetmikoTimeoutException, NetmikoAuthenticationException
|
||||
import yaml
|
||||
|
||||
# Configurazione logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/var/log/glpi_agent_netconfig.log'),
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger('netconfig_backup')
|
||||
|
||||
# Configurazione da file YAML esterno (sicuro, non hardcodato)
|
||||
CONFIG_FILE = os.getenv('NETCONFIG_CONFIG', '/etc/glpi-agent/netconfig_devices.yaml')
|
||||
GLPI_URL = os.getenv('GLPI_URL', 'http://localhost/glpi')
|
||||
GLPI_TOKEN = os.getenv('GLPI_APP_TOKEN', '')
|
||||
AGENT_TOKEN = os.getenv('NETCONFIG_AGENT_TOKEN', 'change_me_in_production')
|
||||
TIMEOUT = int(os.getenv('NETCONFIG_TIMEOUT', '30'))
|
||||
RETRIES = int(os.getenv('NETCONFIG_RETRIES', '2'))
|
||||
|
||||
|
||||
def load_devices_config(path: str) -> List[Dict]:
|
||||
"""Carica configurazione dispositivi da YAML"""
|
||||
if not os.path.exists(path):
|
||||
logger.error(f"Config file not found: {path}")
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
data = yaml.safe_load(f)
|
||||
return data.get('devices', [])
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading config: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_device_connection_params(device: Dict) -> Dict:
|
||||
"""Prepara parametri di connessione per netmiko"""
|
||||
device_type_map = {
|
||||
'cisco_ios': 'cisco_ios',
|
||||
'cisco_nxos': 'cisco_nxos',
|
||||
'hp_comware': 'hp_comware',
|
||||
'juniper_junos': 'juniper_junos',
|
||||
'arista_eos': 'arista_eos',
|
||||
'paloalto_panos': 'paloalto_panos',
|
||||
}
|
||||
|
||||
return {
|
||||
'device_type': device_type_map.get(device['platform'], 'cisco_ios'),
|
||||
'host': device['ip'],
|
||||
'username': device.get('username', os.getenv('NETCONFIG_DEFAULT_USER')),
|
||||
'password': device.get('password', os.getenv('NETCONFIG_DEFAULT_PASS')),
|
||||
'secret': device.get('enable_password', os.getenv('NETCONFIG_ENABLE_PASS')),
|
||||
'port': device.get('port', 22),
|
||||
'timeout': TIMEOUT,
|
||||
'session_log': None,
|
||||
'global_delay_factor': device.get('delay_factor', 1),
|
||||
}
|
||||
|
||||
|
||||
def fetch_config(device: Dict) -> Optional[str]:
|
||||
"""Recupera configurazione via SSH con netmiko"""
|
||||
params = get_device_connection_params(device)
|
||||
|
||||
for attempt in range(RETRIES + 1):
|
||||
try:
|
||||
logger.info(f"Connecting to {device['name']} ({params['host']}) [attempt {attempt+1}]")
|
||||
connection = ConnectHandler(**params)
|
||||
|
||||
# Abilita modalità enable se necessaria
|
||||
if params.get('secret'):
|
||||
connection.enable()
|
||||
|
||||
# Comando di show config (personalizzabile per piattaforma)
|
||||
command = device.get('command', 'show running-config')
|
||||
config = connection.send_command(command, expect_string=r'#|\$', max_loops=150)
|
||||
|
||||
connection.disconnect()
|
||||
|
||||
# Pulizia output da caratteri di controllo
|
||||
config = config.strip()
|
||||
config = '\n'.join(line.rstrip() for line in config.splitlines())
|
||||
|
||||
logger.info(f"Config fetched successfully for {device['name']}")
|
||||
return config
|
||||
|
||||
except NetmikoAuthenticationException as e:
|
||||
logger.error(f"Auth failed for {device['name']}: {e}")
|
||||
break # Non ritentare se auth fallisce
|
||||
except NetmikoTimeoutException as e:
|
||||
logger.warning(f"Timeout for {device['name']}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error for {device['name']}: {e}")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def send_to_glpi(device_id: int, config: str) -> bool:
|
||||
"""Invia configurazione a GLPI plugin endpoint"""
|
||||
url = f"{GLPI_URL}/plugins/netconfig/ajax/agent_receive.php"
|
||||
|
||||
payload = {
|
||||
'device_id': device_id,
|
||||
'config': config,
|
||||
'token': AGENT_TOKEN,
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'agent_version': '1.0.0'
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'App-Token': GLPI_TOKEN, # Opzionale: autenticazione API GLPI
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
url,
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=30,
|
||||
verify=os.getenv('SSL_VERIFY', 'true').lower() == 'true'
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
logger.info(f"GLPI response: {result.get('message', 'OK')}")
|
||||
return result.get('status') == 'ok'
|
||||
else:
|
||||
logger.error(f"GLPI HTTP {response.status_code}: {response.text}")
|
||||
return False
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Request to GLPI failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def calculate_hash(config: str) -> str:
|
||||
"""Calcola SHA256 della configurazione"""
|
||||
return hashlib.sha256(config.encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def run_task():
|
||||
"""Entry point della task"""
|
||||
logger.info("=== NetConfig Backup Task Started ===")
|
||||
|
||||
devices = load_devices_config(CONFIG_FILE)
|
||||
if not devices:
|
||||
logger.warning("No devices configured, exiting")
|
||||
return
|
||||
|
||||
success_count = 0
|
||||
for device in devices:
|
||||
try:
|
||||
device_name = device.get('name', 'unknown')
|
||||
device_id = device.get('glpi_id') # ID in glpi_networkdevices
|
||||
|
||||
if not device_id:
|
||||
logger.warning(f"Skipping {device_name}: missing glpi_id")
|
||||
continue
|
||||
|
||||
config = fetch_config(device)
|
||||
if not config:
|
||||
logger.warning(f"Failed to fetch config for {device_name}")
|
||||
continue
|
||||
|
||||
# Invia a GLPI solo se diverso dall'ultimo (controllo lato server, ma ottimizzazione client)
|
||||
if send_to_glpi(device_id, config):
|
||||
success_count += 1
|
||||
logger.info(f"✓ {device_name} - Config saved")
|
||||
else:
|
||||
logger.error(f"✗ {device_name} - Failed to save to GLPI")
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Unhandled error for device {device.get('name')}")
|
||||
|
||||
logger.info(f"=== Task Completed: {success_count}/{len(devices)} devices processed ===")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_task()
|
||||
@@ -0,0 +1,25 @@
|
||||
devices:
|
||||
- name: "SW-Core-01"
|
||||
ip: "10.0.0.1"
|
||||
platform: "cisco_ios"
|
||||
glpi_id: 123 # ID in glpi_networkdevices
|
||||
username: "backup_user"
|
||||
# password: "xxx" # Meglio usare variabili d'ambiente o vault
|
||||
enable_password: "enable_secret"
|
||||
command: "show running-config"
|
||||
delay_factor: 2
|
||||
|
||||
- name: "SW-Access-05"
|
||||
ip: "10.0.0.5"
|
||||
platform: "hp_comware"
|
||||
glpi_id: 124
|
||||
username: "admin"
|
||||
command: "display current-configuration"
|
||||
|
||||
|
||||
|
||||
|
||||
# 🔐 Best Practice: Non inserire password nel YAML. Usa:
|
||||
# Variabili d'ambiente: NETCONFIG_DEFAULT_PASS
|
||||
# Vault esterno (HashiCorp Vault, AWS Secrets Manager)
|
||||
# File con permessi 600 leggibile solo dall'utente dell'agent
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
// /usr/share/glpi_agent/plugins/netconfig_backup.php
|
||||
require_once '/path/to/glpi/vendor/autoload.php';
|
||||
|
||||
use PluginNetconfig\Api\GlpiApiClient;
|
||||
|
||||
// Carica config da YAML/JSON
|
||||
$devices = yaml_parse_file('/etc/glpi-agent/netconfig_devices.yaml')['devices'] ?? [];
|
||||
|
||||
$api = new GlpiApiClient(
|
||||
baseUrl: getenv('GLPI_URL') ?: 'http://localhost/glpi',
|
||||
appToken: getenv('GLPI_APP_TOKEN'),
|
||||
userToken: getenv('GLPI_USER_TOKEN') // Più sicuro di username/password
|
||||
);
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$config = fetchConfigViaNetmikoPhp($device); // Funzione custom con phpseclib3
|
||||
if ($config) {
|
||||
$api->sendConfig($device['glpi_id'], $config, getenv('NETCONFIG_AGENT_TOKEN'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Ricarica configurazione supervisor
|
||||
sudo supervisorctl reread
|
||||
sudo supervisorctl update
|
||||
|
||||
# Esegui manualmente la task
|
||||
sudo supervisorctl start glpi-netconfig
|
||||
|
||||
# Monitora log in tempo reale
|
||||
sudo tail -f /var/log/glpi_netconfig_supervisor.log
|
||||
@@ -0,0 +1,11 @@
|
||||
# /etc/supervisor/conf.d/glpi-netconfig.conf
|
||||
[program:glpi-netconfig]
|
||||
command=/usr/bin/python3 /usr/share/glpi_agent/plugins/netconfig_backup.py
|
||||
directory=/usr/share/glpi_agent/plugins
|
||||
user=glpi-agent
|
||||
autostart=false # Non partire all'avvio, esegui solo on-demand
|
||||
autorestart=false
|
||||
startsecs=0
|
||||
stdout_logfile=/var/log/glpi_netconfig_supervisor.log
|
||||
stderr_logfile=/var/log/glpi_netconfig_supervisor_err.log
|
||||
environment=NETCONFIG_CONFIG="/etc/glpi-agent/netconfig_devices.yaml",GLPI_URL="https://glpi.example.com",NETCONFIG_AGENT_TOKEN="your_secure_token"
|
||||
@@ -0,0 +1,48 @@
|
||||
# /etc/systemd/system/glpi-netconfig.service
|
||||
[Unit]
|
||||
Description=GLPI NetConfig Backup Task
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=glpi-agent
|
||||
ExecStart=/usr/bin/python3 /usr/share/glpi_agent/plugins/netconfig_backup.py
|
||||
Environment="NETCONFIG_CONFIG=/etc/glpi-agent/netconfig_devices.yaml"
|
||||
Environment="GLPI_URL=https://glpi.example.com"
|
||||
Environment="NETCONFIG_AGENT_TOKEN=your_secure_token"
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- altro ini (bho)
|
||||
|
||||
|
||||
# /etc/systemd/system/glpi-netconfig.timer
|
||||
[Unit]
|
||||
Description=Run GLPI NetConfig Backup every 6 hours
|
||||
Requires=glpi-netconfig.service
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 02/6:00:00
|
||||
Persistent=true
|
||||
RandomizedDelaySec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
||||
|
||||
---
|
||||
# attivazioen disattivazioen timer
|
||||
|
||||
# Attiva e avvia il timer
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now glpi-netconfig.timer
|
||||
|
||||
# Verifica stato
|
||||
systemctl list-timers | grep netconfig
|
||||
journalctl -u glpi-netconfig.service -f
|
||||
@@ -0,0 +1,3 @@
|
||||
# /etc/cron.d/glpi-netconfig
|
||||
# Esegui backup ogni 6 ore alle 02:00, 08:00, 14:00, 20:00
|
||||
0 2,8,14,20 * * * glpi-agent /usr/bin/python3 /usr/share/glpi_agent/plugins/netconfig_backup.py >> /var/log/glpi_netconfig_cron.log 2>&1
|
||||
Reference in New Issue
Block a user