Monitorizar un sistema de virtualización Proxmox mediante Pandora FMS. Monitorear estado del clúster Proxmox, de los OSD del almacenamiento hiperconvergente Ceph, CPU, RAM, procesos, cron, número de máquinas virtuales y contenedores, servicios Ceph, PVE.
- Requisitos para monitorizar Proxmox con Pandora FMS.
- Instalar Pandora Agent en todos los nodos del clúster Proxmox.
- Crear fichero Python para monitorizar el clúster Proxmox y almacenamiento Ceph.
- Comprobar resultado de monitorización de Proxmox en Pandora FMS.
Requisitos para monitorizar Proxmox con Pandora FMS
Necesitaremos disponer de un sistema de virtualización Proxmox. En el estudio de caso que nos ocupa, el sistema de virtualización está formado por tres nodos con Proxmox VE 8.4.0, con almacenamiento hiperconvergente (se comparten todos los discos duros de todos los nodos) con Ceph.
En el siguiente enlace explicamos cómo desplegar Proxmox en un equipo físico para montar un sistema de virtualización:
En este otro tutorial explicamos cómo montar un clúster Proxmox de varios nodos:
Y otro artículo sobre cómo desplegar un almacenamiento hiperconvergente con Ceph en Proxmox:
Por otro lado, dispondremos de un servidor con Pandora FMS. En el siguiente enlace explicamos cómo montar un servidor de monitorización con Pandora FMS:
Instalar Pandora Agent en todos los nodos del clúster Proxmox
Dado que Proxmox se despliega sobre Linux Debian 12, instalaremos el agente de Pandora FMS en cada nodo del clúster Proxmox. Para ello, seguiremos las instrucciones del siguiente tutorial:
Básicamente, desde el shell de comandos de cada nodo, ejecutaremos:
|
1 |
apt-get install pandorafms-agent |

Configuraremos el fichero pandora_agent.conf de cada nodo, para introducir los datos de conexión al servidor de Pandora (en nuestro caso con la IP 192.168.100), con el comando:
|
1 |
nano /etc/pandorafms/pandora_agent.conf |

Un ejemplo de contenido del fichero pandora_agent.conf:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
server_ip 192.168.1.100 server_path /var/spool/pandorafms/data_in temporal /tmp logfile /var/log/pandorafms/pandora_agent.log interval 300 debug 0 udp_server 0 agent_name proxmox2 description Nodo 2 del cluster Proxmox VE address 192.168.1.59 server_port 41121 transfer_mode tentacle remote_config 0 xml_buffer 1 # Modulos module_begin module_name Carga_CPU module_type generic_data module_interval 1 module_exec vmstat 1 2 | tail -1 | awk '{ print $13 }' module_max 100 module_min 0 module_description Uso de CPU (%) module_min_warning 70 module_max_warning 90 module_min_critical 91 module_max_critical 100 module_unit % module_end module_begin module_name Carga_CPU_media_ultimo_minuto module_type generic_data module_exec cat /proc/loadavg | cut -d' ' -f1 module_description Carga media de la CPU en el ultimo minuto module_end module_begin module_name Memoria_Cache_Libre module_type generic_data module_exec free -m | grep buffers/cache | awk '{print $4}' module_description Memoria cache libre (MB) module_min_warning 500 module_max_warning 600 module_min_critical 100 module_max_critical 499 module_unit MB module_end module_begin module_name Numero_Procesos module_type generic_data module_exec ps -A | tail --lines=+5 | wc -l module_description Numero de procesos en ejecucion module_min_warning 150 module_max_warning 249 module_min_critical 250 module_max_critical 300 module_unit processes module_end module_begin module_name Servicio_SSH module_type generic_proc module_exec ps -Af | grep sshd | grep -v "grep" | wc -l module_description Servicio SSH module_end module_begin module_name Ultimo_Inicio_Sesion module_type async_string module_exec last | head -1 module_description Ultimo usuario que inicio sesion module_end module_begin module_name Tareas_Cron module_type async_string module_precondition =~ .*cron.* ps aux | grep cron module_exec ls -l /etc/cron.d | awk 'NR>1 {print $0}' | wc -l module_description Numero de tareas en el Cron module_unit files module_end module_plugin pandora_df |
Podremos añadir todos los módulos que queramos a cada nodo del clúster Proxmox, en el ejemplo anterior mostramos algunos estándar: tareas cron, último inicio de sesión, servicio SSH, número de procesos, memoria caché libre, uso de CPU, uso de disco, etc. Más adelante añadiremos un módulo más al fichero pandora_agent.conf, para monitorizar el clúster Ceph de almacenamiento.
Crear fichero Python para monitorizar el clúster Proxmox y almacenamiento Ceph
Crearemos un fichero llamado proxmox.py en la carpeta de cada nodo Proxmox:
/usr/share/pandorafms/agent/plugins/
Para ello, ejecutaremos el comando:
|
1 |
nano /usr/share/pandorafms/agent/plugins/proxmox.py |
Copiaremos y pegaremos el siguiente script Python. Por supuesto, se trata de un ejemplo con unos datos de salida para monitorizar, que se pueden modificar y personalizar. En el caso de los umbrales de algunos de los módulos, que definen su estado (normal, alerta o crítico), podremos también ajustarlos a nuestras necesidades:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import subprocess import json import os import sys from datetime import datetime """Ejecutar un comando y devolver su salida""" def EjecutarComando(comando): try: salida = subprocess.check_output(comando, shell=True, stderr=subprocess.DEVNULL, universal_newlines=True, timeout=10) return salida.strip() except (subprocess.CalledProcessError, subprocess.TimeoutExpired, Exception): return None """Obtener información de Proxmox VE específica del nodo""" def ObtenerInfoProxmoxNodo(): info = {} # Obtener nombre del nodo actual y estado comando_nodo = "pvesh get /nodes/localhost/status --output-format json 2>/dev/null" estadoNodo = EjecutarComando(comando_nodo) if estadoNodo: try: nodo_info = json.loads(estadoNodo) info['Nombre_Nodo'] = nodo_info.get('node', 'Desconocido') info['Estado_Nodo'] = 'online' if nodo_info.get('status') == 'online' else 'offline' # Información de memoria del nodo info['Memoria_Total_GB'] = round(nodo_info.get('memory', {}).get('total', 0) / (1024**3), 2) info['Memoria_Usada_GB'] = round(nodo_info.get('memory', {}).get('used', 0) / (1024**3), 2) if info['Memoria_Total_GB'] > 0: info['Memoria_Porcentaje'] = round((info['Memoria_Usada_GB'] / info['Memoria_Total_GB']) * 100, 2) else: info['Memoria_Porcentaje'] = 0 except (json.JSONDecodeError, KeyError, AttributeError): info['Nombre_Nodo'] = 'Desconocido' info['Estado_Nodo'] = 'desconocido' # VMs en este nodo comando_vms = "pvesh get /nodes/localhost/qemu --output-format json 2>/dev/null" vms_nodo = EjecutarComando(comando_vms) if vms_nodo: try: vms = json.loads(vms_nodo) info['Num_MV_Nodo'] = len(vms) info['Num_MV_Iniciadas_Nodo'] = len([vm for vm in vms if vm.get('status') == 'running']) info['Num_MV_Paradas_Nodo'] = len([vm for vm in vms if vm.get('status') == 'stopped']) except (json.JSONDecodeError, KeyError): info['Num_MV_Nodo'] = 0 info['Num_MV_Iniciadas_Nodo'] = 0 info['Num_MV_Paradas_Nodo'] = 0 # Contenedores en este nodo comando_ct = "pvesh get /nodes/localhost/lxc --output-format json 2>/dev/null" cts_nodo = EjecutarComando(comando_ct) if cts_nodo: try: cts = json.loads(cts_nodo) info['Num_Contenedores_Nodo'] = len(cts) info['Num_Contenedores_Iniciados_Nodo'] = len([ct for ct in cts if ct.get('status') == 'running']) info['Num_Contenedores_Parados_Nodo'] = len([ct for ct in cts if ct.get('status') == 'stopped']) except (json.JSONDecodeError, KeyError): info['Num_Contenedores_Nodo'] = 0 info['Num_Contenedores_Iniciados_Nodo'] = 0 info['Num_Contenedores_Parados_Nodo'] = 0 # Información del cluster comando = "pvesh get /cluster/status --output-format json 2>/dev/null" estadoCluster = EjecutarComando(comando) if estadoCluster: try: estadoCluster = json.loads(estadoCluster) # Buscar información del quorum quorum_info = next((item for item in estadoCluster if item.get('type') == 'quorum'), None) if quorum_info: info['cluster_ConQuorum'] = quorum_info.get('quorate', False) else: info['cluster_ConQuorum'] = False # Contar nodos nodos = [n for n in estadoCluster if n.get('type') == 'node'] info['Numero_Nodos_Cluster'] = len(nodos) info['Numero_Nodos_Online_Cluster'] = len([n for n in nodos if n.get('online', False)]) info['Numero_Nodos_Offline_Cluster'] = len([n for n in nodos if not n.get('online', True)]) except (json.JSONDecodeError, KeyError, StopIteration): info['cluster_ConQuorum'] = False info['Numero_Nodos_Cluster'] = 0 info['Numero_Nodos_Online_Cluster'] = 0 info['Numero_Nodos_Offline_Cluster'] = 0 return info """Obtener información de storages Proxmox en el nodo""" def ObtenerStoragesProxmox(): info = {'storages': []} comando = "pvesh get /storage --output-format json 2>/dev/null" storages = EjecutarComando(comando) if not storages: return info try: storages = json.loads(storages) for storage in storages: storage_name = storage['storage'] storage_type = storage.get('type', 'desconocido') storage_info = { 'nombre': storage_name, 'tipo': storage_type, 'contenido': ', '.join(storage.get('content', [])), 'activado': storage.get('enabled', 0), 'shared': storage.get('shared', 0) } # Solo intentar obtener información de espacio para storages locales if storage_type in ['dir', 'lvm', 'lvmthin', 'zfspool', 'zfs']: # Usar df directamente para evitar problemas con pvesh if os.path.exists(f"/var/lib/vz") and 'local' in storage_name.lower(): # Para storage local, usar df en /var/lib/vz comando_df = "df -k /var/lib/vz | tail -1" df_output = EjecutarComando(comando_df) if df_output: parts = df_output.split() if len(parts) >= 5: total_kb = int(parts[1]) used_kb = int(parts[2]) available_kb = int(parts[3]) storage_info['total_gb'] = round(total_kb / (1024*1024), 2) storage_info['used_gb'] = round(used_kb / (1024*1024), 2) storage_info['available_gb'] = round(available_kb / (1024*1024), 2) if total_kb > 0: storage_info['porcentaje_uso'] = round((used_kb / total_kb) * 100, 2) info['storages'].append(storage_info) except (json.JSONDecodeError, KeyError, ValueError): pass return info """Obtener información básica de Ceph""" def ObtenerInformacionCeph(): info = { 'ceph_installed': False, 'estadoCeph': 'NO_INSTALADO', 'osd_total': 0, 'osd_up': 0, 'osd_down': 0 } if not os.path.exists('/etc/ceph/ceph.conf'): return info info['ceph_installed'] = True # Estado de Ceph comando = "ceph health --format json 2>/dev/null || ceph health 2>/dev/null" estadoCeph = EjecutarComando(comando) if estadoCeph: try: # Intentar parsear como JSON estado_json = json.loads(estadoCeph) info['estadoCeph'] = estado_json.get('status', 'UNKNOWN') except json.JSONDecodeError: # Si no es JSON, usar el output directo info['estadoCeph'] = estadoCeph.split()[0] if estadoCeph else 'UNKNOWN' # Información básica de OSDs comando_osd = "ceph osd stat 2>/dev/null || echo '0 osds: 0 up, 0 in'" osd_stat = EjecutarComando(comando_osd) if osd_stat: # Parsear formato: "12 osds: 12 up, 12 in" try: parts = osd_stat.split() if len(parts) >= 5: info['osd_total'] = int(parts[0]) info['osd_up'] = int(parts[2].rstrip(',')) info['osd_down'] = info['osd_total'] - info['osd_up'] except (ValueError, IndexError): pass # Información básica de pools comando_pools = "ceph osd pool ls 2>/dev/null" pools_list = EjecutarComando(comando_pools) if pools_list: pools = [p.strip() for p in pools_list.split('n') if p.strip()] info['pools'] = pools info['num_pools'] = len(pools) # Información básica de CephFS comando_fs = "ceph fs ls 2>/dev/null" fs_info = EjecutarComando(comando_fs) if fs_info and "No filesystem" not in fs_info: fs_list = [line.strip() for line in fs_info.split('n') if line.strip()] info['fs_list'] = fs_list info['num_cephfs'] = len(fs_list) return info """Verificar estado de los servicios Proxmox en el nodo""" def VerificarServiciosNodo(): info = {} # Servicios Proxmox servicios_proxmox = ['pve-cluster', 'pvedaemon', 'pveproxy', 'pvestatd'] info['servicios_proxmox'] = {} for servicio in servicios_proxmox: comando = f"systemctl is-active {servicio} 2>/dev/null" estado = EjecutarComando(comando) info['servicios_proxmox'][servicio] = estado if estado else 'unknown' # Verificar si Ceph está instalado para ver sus servicios if os.path.exists('/etc/ceph/ceph.conf'): info['servicios_ceph'] = {} # Solo verificar servicios básicos de Ceph servicios_ceph = ['ceph-mon', 'ceph-mgr', 'ceph-osd'] for servicio in servicios_ceph: # Verificar si algún servicio de este tipo está activo comando = f"systemctl list-units --all 'ceph*' 2>/dev/null | grep {servicio} | grep running || echo ''" salida = EjecutarComando(comando) if salida and 'running' in salida: info['servicios_ceph'][servicio] = 'active' else: info['servicios_ceph'][servicio] = 'inactive' return info """Generar formato XML para Pandora FMS""" def generarModuloPandora(name, mtype, description, value, min_warn=None, max_warn=None, min_crit=None, max_crit=None, str_warning=None, str_critical=None, module_group=None): module = [] module.append("<module>") module.append(f"<name><![CDATA[{name}]]></name>") module.append(f"<type><![CDATA[{mtype}]]></type>") module.append(f"<description>{description}</description>") module.append(f"<data><![CDATA[{value}]]></data>") if module_group: module.append(f"<module_group><![CDATA[{module_group}]]></module_group>") if min_warn is not None: module.append(f"<min_warning>{min_warn}</min_warning>") if max_warn is not None: module.append(f"<max_warning>{max_warn}</max_warning>") if min_crit is not None: module.append(f"<min_critical>{min_crit}</min_critical>") if max_crit is not None: module.append(f"<max_critical>{max_crit}</max_critical>") if str_warning is not None: module.append(f"<str_warning><![CDATA[{str_warning}]]></str_warning>") if str_critical is not None: module.append(f"<str_critical><![CDATA[{str_critical}]]></str_critical>") module.append("</module>") return "n".join(module) """Generar la salida completa para Pandora FMS""" def generarXMLAgentePandora(data_proxmox, data_storages, data_ceph, data_servicios): output = [] timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Cabecera del agente output.append(f"# Agente: Proxmox_Ceph_Monitor") output.append(f"# Versión: 1.1") output.append(f"# Fecha: {timestamp}") output.append("") # Módulos de información del nodo Proxmox output.append(generarModuloPandora( name="Nombre_Nodo", mtype="generic_data_string", description="Nombre del nodo Proxmox", value=data_proxmox.get('Nombre_Nodo', 'Desconocido'), module_group="Proxmox Nodo" )) output.append(generarModuloPandora( name="Estado_Nodo", mtype="generic_data_string", description="Estado del nodo Proxmox", value=data_proxmox.get('Estado_Nodo', 'desconocido'), str_warning="!online", str_critical="!online", module_group="Proxmox Nodo" )) if 'Memoria_Porcentaje' in data_proxmox: output.append(generarModuloPandora( name="Memoria_Proxmox_Porcentaje", mtype="generic_data", description="Porcentaje de memoria usado por Proxmox", value=data_proxmox.get('Memoria_Porcentaje', 0), max_warn=85, max_crit=95, module_group="Proxmox Nodo" )) # VMs en este nodo output.append(generarModuloPandora( name="VMs_Nodo_Total", mtype="generic_data", description="Numero total de VMs en este nodo", value=data_proxmox.get('Num_MV_Nodo', 0), module_group="Proxmox VMs Nodo" )) output.append(generarModuloPandora( name="VMs_Nodo_Running", mtype="generic_data", description="Numero de VMs ejecutandose en este nodo", value=data_proxmox.get('Num_MV_Iniciadas_Nodo', 0), module_group="Proxmox VMs Nodo" )) output.append(generarModuloPandora( name="VMs_Nodo_Stopped", mtype="generic_data", description="Numero de VMs paradas en este nodo", value=data_proxmox.get('Num_MV_Paradas_Nodo', 0), module_group="Proxmox VMs Nodo" )) # Contenedores en este nodo output.append(generarModuloPandora( name="Contenedores_Nodo_Total", mtype="generic_data", description="Numero total de contenedores en este nodo", value=data_proxmox.get('Num_Contenedores_Nodo', 0), module_group="Proxmox Contenedores Nodo" )) output.append(generarModuloPandora( name="Contenedores_Nodo_Running", mtype="generic_data", description="Numero de contenedores ejecutandose en este nodo", value=data_proxmox.get('Num_Contenedores_Iniciados_Nodo', 0), module_group="Proxmox Contenedores Nodo" )) output.append(generarModuloPandora( name="Contenedores_Nodo_Stopped", mtype="generic_data", description="Numero de contenedores parados en este nodo", value=data_proxmox.get('Num_Contenedores_Parados_Nodo', 0), module_group="Proxmox Contenedores Nodo" )) # Información del cluster output.append(generarModuloPandora( name="Cluster_ConQuorum", mtype="generic_proc", description="Estado del quorum del cluster (1=Con quorum, 0=Sin quorum)", value=1 if data_proxmox.get('cluster_ConQuorum', False) else 0, module_group="Proxmox Cluster" )) output.append(generarModuloPandora( name="Nodos_Cluster_Total", mtype="generic_data", description="Numero total de nodos en el cluster", value=data_proxmox.get('Numero_Nodos_Cluster', 0), module_group="Proxmox Cluster" )) output.append(generarModuloPandora( name="Nodos_Cluster_Online", mtype="generic_data", description="Numero de nodos online en el cluster", value=data_proxmox.get('Numero_Nodos_Online_Cluster', 0), module_group="Proxmox Cluster" )) output.append(generarModuloPandora( name="Nodos_Cluster_Offline", mtype="generic_data", description="Numero de nodos offline en el cluster", value=data_proxmox.get('Numero_Nodos_Offline_Cluster', 0), max_warn=0, max_crit=1, module_group="Proxmox Cluster" )) # Servicios Proxmox for servicio, estado in data_servicios.get('servicios_proxmox', {}).items(): output.append(generarModuloPandora( name=f"Servicio_{servicio}", mtype="generic_data_string", description=f"Estado del servicio {servicio}", value=estado, str_warning="!active", str_critical="!active", module_group="Proxmox Servicios" )) # Ceph (si está instalado) if data_ceph['ceph_installed']: # Estado general de Ceph output.append(generarModuloPandora( name="Ceph_Estado", mtype="generic_data_string", description="Estado de servicio Ceph", value=data_ceph.get('estadoCeph', 'UNKNOWN'), str_warning="HEALTH_WARN", str_critical="HEALTH_ERR", module_group="Ceph" )) # Estadísticas de OSDs output.append(generarModuloPandora( name="Ceph_OSD_Total", mtype="generic_data", description="Numero total de OSD en Ceph", value=data_ceph.get('osd_total', 0), module_group="Ceph OSD" )) output.append(generarModuloPandora( name="Ceph_OSD_Up", mtype="generic_data", description="Numero de OSD activos en Ceph", value=data_ceph.get('osd_up', 0), module_group="Ceph OSD" )) output.append(generarModuloPandora( name="Ceph_OSD_Down", mtype="generic_data", description="Numero de OSD inactivos en Ceph", value=data_ceph.get('osd_down', 0), max_warn=0, max_crit=0, module_group="Ceph OSD" )) # Pools de Ceph if 'num_pools' in data_ceph: output.append(generarModuloPandora( name="Ceph_Pools_Total", mtype="generic_data", description="Numero total de pools en Ceph", value=data_ceph.get('num_pools', 0), module_group="Ceph Pools" )) # CephFS if 'num_cephfs' in data_ceph and data_ceph['num_cephfs'] > 0: output.append(generarModuloPandora( name="CephFS_Total", mtype="generic_data", description="Numero de sistemas CephFS", value=data_ceph.get('num_cephfs', 0), module_group="CephFS" )) # Servicios Ceph if 'servicios_ceph' in data_servicios: for servicio, estado in data_servicios['servicios_ceph'].items(): output.append(generarModuloPandora( name=f"Servicio_{servicio}", mtype="generic_data_string", description=f"Estado del servicio {servicio} de Ceph", value=estado, str_warning="!active", str_critical="!active", module_group="Ceph Servicios" )) return "n".join(output) def main(): # Recolectar datos específicos de Proxmox y Ceph data_proxmox = ObtenerInfoProxmoxNodo() data_storages = ObtenerStoragesProxmox() data_ceph = ObtenerInformacionCeph() data_servicios = VerificarServiciosNodo() # Generar salida try: agent_output = generarXMLAgentePandora(data_proxmox, data_storages, data_ceph, data_servicios) print(agent_output) except Exception as e: print(f"Error al generar la salida: {str(e)}", file=sys.stderr) sys.exit(1) if __name__ == '__main__': main() |
Una vez generado el fichero proxmox.py, le asignaremos el permiso de ejecución, con el comando:
|
1 |
chmod +x /usr/share/pandorafms/agent/plugins/proxmox.py |
Antes de ejecutar este fichero en Pandora FMS Agent, es conveniente probarlo desde el shell de Linux de uno de los nodos Proxmox, para verificar que funciona correctamente. Para ello, ejecutaremos el comando:
|
1 |
./usr/share/pandorafms/agent/plugins/proxmox.py |
El comando debe devolver algo parecido a esto (que es el formato XML que reconoce Pandora FMS):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# Agente: Proxmox_Ceph_Monitor # Versión: 1.1 # Fecha: 2025-12-26 12:45:15 <module> <name><![CDATA[Nombre_Nodo]]></name> <type><![CDATA[generic_data_string]]></type> <description>Nombre del nodo Proxmox</description> <data><![CDATA[Desconocido]]></data> <module_group><![CDATA[Proxmox Nodo]]></module_group> </module> <module> <name><![CDATA[Estado_Nodo]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del nodo Proxmox</description> <data><![CDATA[offline]]></data> <module_group><![CDATA[Proxmox Nodo]]></module_group> <str_warning><![CDATA[!online]]></str_warning> <str_critical><![CDATA[!online]]></str_critical> </module> <module> <name><![CDATA[Memoria_Proxmox_Porcentaje]]></name> <type><![CDATA[generic_data]]></type> <description>Porcentaje de memoria usado por Proxmox</description> <data><![CDATA[18.02]]></data> <module_group><![CDATA[Proxmox Nodo]]></module_group> <max_warning>85</max_warning> <max_critical>95</max_critical> </module> <module> <name><![CDATA[VMs_Nodo_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero total de VMs en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox VMs Nodo]]></module_group> </module> <module> <name><![CDATA[VMs_Nodo_Running]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de VMs ejecutandose en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox VMs Nodo]]></module_group> </module> <module> <name><![CDATA[VMs_Nodo_Stopped]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de VMs paradas en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox VMs Nodo]]></module_group> </module> <module> <name><![CDATA[Contenedores_Nodo_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero total de contenedores en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox Contenedores Nodo]]></module_group> </module> <module> <name><![CDATA[Contenedores_Nodo_Running]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de contenedores ejecutandose en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox Contenedores Nodo]]></module_group> </module> <module> <name><![CDATA[Contenedores_Nodo_Stopped]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de contenedores parados en este nodo</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox Contenedores Nodo]]></module_group> </module> <module> <name><![CDATA[Cluster_ConQuorum]]></name> <type><![CDATA[generic_proc]]></type> <description>Estado del quorum del cluster (1=Con quorum, 0=Sin quorum)</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox Cluster]]></module_group> </module> <module> <name><![CDATA[Nodos_Cluster_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero total de nodos en el cluster</description> <data><![CDATA[3]]></data> <module_group><![CDATA[Proxmox Cluster]]></module_group> </module> <module> <name><![CDATA[Nodos_Cluster_Online]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de nodos online en el cluster</description> <data><![CDATA[3]]></data> <module_group><![CDATA[Proxmox Cluster]]></module_group> </module> <module> <name><![CDATA[Nodos_Cluster_Offline]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de nodos offline en el cluster</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Proxmox Cluster]]></module_group> <max_warning>0</max_warning> <max_critical>1</max_critical> </module> <module> <name><![CDATA[Servicio_pve-cluster]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio pve-cluster</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Proxmox Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Servicio_pvedaemon]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio pvedaemon</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Proxmox Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Servicio_pveproxy]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio pveproxy</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Proxmox Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Servicio_pvestatd]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio pvestatd</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Proxmox Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Ceph_Estado]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado de servicio Ceph</description> <data><![CDATA[HEALTH_WARN]]></data> <module_group><![CDATA[Ceph]]></module_group> <str_warning><![CDATA[HEALTH_WARN]]></str_warning> <str_critical><![CDATA[HEALTH_ERR]]></str_critical> </module> <module> <name><![CDATA[Ceph_OSD_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero total de OSD en Ceph</description> <data><![CDATA[12]]></data> <module_group><![CDATA[Ceph OSD]]></module_group> </module> <module> <name><![CDATA[Ceph_OSD_Up]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de OSD activos en Ceph</description> <data><![CDATA[12]]></data> <module_group><![CDATA[Ceph OSD]]></module_group> </module> <module> <name><![CDATA[Ceph_OSD_Down]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de OSD inactivos en Ceph</description> <data><![CDATA[0]]></data> <module_group><![CDATA[Ceph OSD]]></module_group> <max_warning>0</max_warning> <max_critical>0</max_critical> </module> <module> <name><![CDATA[Ceph_Pools_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero total de pools en Ceph</description> <data><![CDATA[4]]></data> <module_group><![CDATA[Ceph Pools]]></module_group> </module> <module> <name><![CDATA[CephFS_Total]]></name> <type><![CDATA[generic_data]]></type> <description>Numero de sistemas CephFS</description> <data><![CDATA[1]]></data> <module_group><![CDATA[CephFS]]></module_group> </module> <module> <name><![CDATA[Servicio_ceph-mon]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio ceph-mon de Ceph</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Ceph Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Servicio_ceph-mgr]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio ceph-mgr de Ceph</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Ceph Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> <module> <name><![CDATA[Servicio_ceph-osd]]></name> <type><![CDATA[generic_data_string]]></type> <description>Estado del servicio ceph-osd de Ceph</description> <data><![CDATA[active]]></data> <module_group><![CDATA[Ceph Servicios]]></module_group> <str_warning><![CDATA[!active]]></str_warning> <str_critical><![CDATA[!active]]></str_critical> </module> |
Si el resultado es similar al anterior, copiaremos el fichero proxmox.py a cada nodo del clúster Proxmox, podremos hacerlo con el comando scp:
|
1 2 |
scp /usr/share/pandorafms/agent/plugins/proxmox.py root@192.168.1.59:/usr/share/pandorafms/agent/plugins/ proxmox.py |
En el caso anterior estamos copiando el fichero proxmox.py al nodo con IP 192.168.1.59.
Repetiremos el proceso para cada nodo, dejando el fichero proxmox.py en todos los nodos y en la misma carpeta.
Para que Pandora FMS Agent ejecute este fichero, desde cada nodo, editaremos el fichero pandora_agent.conf, con el comando:
|
1 |
nano /etc/pandorafms/pandora_agent.conf |
Y al final del fichero, añadiremos la línea:
module_plugin proxmox.py

Guardaremos los cambios e reiniciaremos el servicio de Pandora Agent en uno de los nodos, para verificar que funciona correctamente:
|
1 |
systemctl restart pandorafms-agent |
Comprobaremos que el servicio Pandora Agent se ha iniciado correctamente con:
|
1 |
systemctl status pandorafms-agent |

Comprobar resultado de monitorización de Proxmox en Pandora FMS
Transcurridos unos segundos, en nuestro servidor de Pandora FMS, deberían aparecernos los nuevos agentes (uno por cada nodo Proxmox) y, cada agente, contendrá todos los módulos especificados en el fichero pandora_agent.conf y los obtenidos por el script Python proxmox.py:

En este caso, monitorizaremos los siguientes agentes:
| Módulo | Descripción |
| /dev/fuse | % de espacio usado en esta unidad (módulo estándar) |
| /dev/mapper/proxmox3–vg-root | % de espacio usado en esta unidad (módulo estándar) |
| /dev/sda1 | % de espacio usado en esta unidad (módulo estándar) |
| Carga_CPU | Uso de CPU (%) (módulo estándar) |
| Carga_CPU_media_ultimo_minuto | Carga media de la CPU en el ultimo minuto (módulo estándar) |
| Ceph_Estado | Estado de servicio Ceph |
| Ceph_OSD_Down | Número de OSD inactivos en Ceph |
| Ceph_OSD_Total | Número total de OSD en Ceph |
| Ceph_OSD_Up | Número de OSD activos en Ceph |
| Ceph_Pools_Total | Número total de pools en Ceph |
| CephFS_Total | Número de sistemas CephFS |
| Cluster_ConQuorum | Estado del quorum del cluster (1=Con quorum, 0=Sin quorum) |
| Contenedores_Nodo_Running | Número de contenedores ejecutándose en este nodo |
| Contenedores_Nodo_Stopped | Número de contenedores parados en este nodo |
| Contenedores_Nodo_Total | Número total de contenedores en este nodo |
| Estado_Nodo | Estado del nodo Proxmox |
| Memoria_Proxmox_Porcentaje | Porcentaje de memoria usado por Proxmox |
| Nodos_Cluster_Offline | Número de nodos offline en el cluster |
| Nodos_Cluster_Online | Número de nodos online en el cluster |
| Nodos_Cluster_Total | Número total de nodos en el cluster |
| Nombre_Nodo | Nombre del nodo Proxmox |
| Numero_Procesos | Número de procesos en ejecución |
| Servicio_ceph-mgr | Estado del servicio ceph-mgr de Ceph |
| Servicio_ceph-mon | Estado del servicio ceph-mon de Ceph |
| Servicio_ceph-osd | Estado del servicio ceph-osd de Ceph |
| Servicio_pve-cluster | Estado del servicio pve-cluster |
| Servicio_pvedaemon | Estado del servicio pvedaemon |
| Servicio_pveproxy | Estado del servicio pveproxy |
| Servicio_pvestatd | Estado del servicio pvestatd |
| Servicio_SSH | Servicio SSH (módulo estándar) |
| Tareas_Cron | Numero de tareas en el Cron (módulo estándar) |
| Ultimo_Inicio_Sesion | Último usuario que inició sesión (módulo estándar) |
| VMs_Nodo_Running | Número de VMs ejecutándose en este nodo |
| VMs_Nodo_Stopped | Número de VMs paradas en este nodo |
| VMs_Nodo_Total | Número total de VMs en este nodo |