Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .requirement-agent
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
psutil
flask
2 changes: 2 additions & 0 deletions .requirement-collector
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

dotenv
73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,72 @@
# monitoring
# monitoring

# Supervision et Monitoring d'Infrastructure Linux

Ce projet fournit une solution simple et efficace pour superviser une infrastructure Linux, en collectant des métriques système via des agents Python et en sauvegardant ces données pour analyse.

---

## 🛠️ Fonctionnalités

### 1. Serveur de Monitoring
- **Endpoints :**
- `/healthcheck` : Vérifie si l'agent est opérationnel.
- `/getmetrics` : Récupère les métriques système (CPU, mémoire, disque, etc.).
- Collecte de métriques en temps réel grâce à `psutil`.

### 2. Script de Supervision
- Collecte des métriques de plusieurs agents en parallèle.
- Gestion dynamique des listes blanche et noire :
- **Liste blanche** : Agents à surveiller.
- **Liste noire** : Agents inaccessibles ou hors service.
- Sauvegarde des métriques dans un fichier JSON (`metrics_data.json`).

---

## 🖥️ Installation

### Prérequis
1. **Python 3.7+** installé.
2. Bibliothèques Python nécessaires :
- `psutil`
- `flask`
- `requests`
- `python-dotenv`

### Installation des dépendances
```bash
pip install -r requirements-agent
pip install -r requirements-collector
```

### Configuration

Créez un fichier .env dans le répertoire principal avec le contenu suivant :

METRICS_FILE=metrics_data.json
INTERVAL_HEALTH_CHECK=3
INTERVAL_METRICS_CHECK=1
WHITELIST_AGENT=127.0.0.1,192.168.1.100
HEALTHROOT=healthcheck
METRICSROOT=getmetrics

### Paramètres principaux :

METRICS_FILE : Nom du fichier JSON où les métriques seront sauvegardées.
INTERVAL_HEALTH_CHECK : Intervalle (en secondes) entre les vérifications de santé.
INTERVAL_METRICS_CHECK : Intervalle (en secondes) entre les collectes de métriques.
WHITELIST_AGENT : Liste des adresses IP des agents autorisés, séparées par des virgules.


### arborescence
├── agent.py # Script Flask pour l'agent
├── supervisor.py # Script de supervision
├── metrics_data.json # Fichier JSON contenant les métriques collectées
├── .env # Variables d'environnement pour la configuration
├── requirements.txt # Liste des dépendances Python
├── README.md # Documentation du projet

### auteur
josselin
heidi
antoine
89 changes: 89 additions & 0 deletions agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/python3
"""
Ce script expose un serveur Flask qui collecte des métriques système et les rend disponibles via un endpoint `/getmetrics`.
Les métriques incluent l'utilisation du CPU, de la mémoire, du disque, le nom du système d'exploitation,
le nom de l'hôte, et l'heure.

Le port d'exécution du serveur doit etre choisis par l'ingénieur sinon le port par defaut est de 5000

Les métriques sont collectées en temps réel à l'aide de la bibliothèque `psutil`.
"""

import psutil
from flask import Flask, jsonify
import platform
from datetime import datetime

# Initialisation de l'application Flask
app = Flask(__name__)

def collect_metrics():
"""
Collecte les métriques système, incluant l'utilisation du CPU, de la mémoire, et du disque,
ainsi que des informations sur le système d'exploitation et le nom de l'hôte.

Returns:
- dict: Un dictionnaire contenant les métriques collectées :
- cpu_usage (str): Pourcentage d'utilisation du CPU.
- memory_usage (str): Pourcentage d'utilisation de la mémoire.
- disk_usage (str): Pourcentage d'utilisation du disque principal.
- os (str): Nom du système d'exploitation.
- hostname (str): Nom de l'hôte de la machine.
- timestamp (str): Horodatage actuel au format ISO 8601.
"""
# Collecte des métriques système
cpu_usage = psutil.cpu_percent(interval=1) # Utilisation CPU en pourcentage
memory = psutil.virtual_memory().percent # Utilisation mémoire en pourcentage
disk = psutil.disk_usage('/').percent # Utilisation disque en pourcentage
os_name = platform.system() # Nom du système d'exploitation
hostname = platform.node() # Nom de la machine (hôte)
timestamp = datetime.now().isoformat() # Horodatage actuel

# Création du dictionnaire contenant les métriques
metrics = {
"cpu_usage": f"{cpu_usage}%",
"memory_usage": f"{memory}%",
"disk_usage": f"{disk}%",
"os": os_name,
"hostname": hostname,
"timestamp": timestamp
}
return metrics

@app.route('/healthcheck', methods=['GET'])
def provide_health():
"""
Endpoint Flask pour fournir un healthcheck.
Lorsqu'une requête POST est reçue sur `/healthcheck`, les métriques système sont collectées et renvoyées en JSON.

Returns:
- tuple: Un tuple contenant :
- Response: La réponse JSON avec les métriques.
- int: Le code HTTP 200 (succès).
"""
return jsonify('OK'),200

@app.route('/getmetrics', methods=['GET'])
def provide_metrics():
"""
Endpoint Flask pour fournir les métriques collectées.
Lorsqu'une requête POST est reçue sur `/getmetrics`, les métriques système sont collectées et renvoyées en JSON.

Returns:
- tuple: Un tuple contenant :
- Response: La réponse JSON avec les métriques.
- int: Le code HTTP 200 (succès).
"""
# Collecte des métriques système
metrics = collect_metrics()
# Renvoi des métriques en réponse à la requête
return jsonify(metrics), 200

if __name__ == '__main__':
"""
Point d'entrée principal du script.
Configure et lance le serveur Flask sur l'adresse `0.0.0.0` et le port 5000 par defaut
"""
# Lancement de l'application Flask
app.run(host='0.0.0.0', port=5000)

198 changes: 161 additions & 37 deletions collector.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,179 @@
from flask import Flask, request, jsonify
import json
import requests
#!/usr/bin/python3

"""
Ce script exécute des vérifications de santé et des récupérations de métriques pour une liste d'agents,
et enregistre ces données dans un fichier JSON. Les vérifications sont effectuées à intervalles réguliers
avec un mécanisme asynchrone pour gérer plusieurs agents simultanément.

Le script vérifie la santé des agents via l'endpoint /healthcheck et récupère les métriques via l'endpoint /metrics.
Les agents sont spécifiés dans un fichier d'environnement (.env) et sont gérés par des listes blanches et noires dynamiques.

Les données collectées sont enregistrées dans un fichier JSON, et des notifications sont émises en cas de problème de connectivité.
"""

from datetime import datetime
import asyncio
import requests
import json
import os
from dotenv import load_dotenv

# Charger les variables d'environnement depuis le fichier .env
load_dotenv()

app = Flask(__name__)
# Fonction pour vérifier si une valeur est positive
def check_positive(value: int) -> bool:
"""
Vérifie si une valeur est positive.

# Fichier où les métriques seront sauvegardées
METRICS_FILE = 'metrics_data.json'
AGENT_URL = 'http://172.21.31.251:5001/metrics'
Args:
- value (int): La valeur à vérifier.

def load_metrics():
Returns:
- bool: True si la valeur est positive, sinon False.
"""
Charge les métriques sauvegardées depuis un fichier.
try:
return value > 0
except ValueError:
return False

# Lecture des variables d'environnement
METRICS_FILE:str = os.getenv('METRICS_FILE', 'metrics_data.json') # Fichier où les métriques sont sauvegardées
INTERVAL_HEALTH_CHECK:int = int(os.getenv('INTERVAL_HEALTH_CHECK', 3)) # Intervalle pour les vérifications de santé (en secondes)
INTERVAL_METRICS_CHECK:int = int(os.getenv('INTERVAL_METRICS_CHECK', 1)) # Intervalle pour la récupération des métriques (en secondes)
HEALTHROOT:str = os.getenv('HEALTHROOT', 'healthcheck') # Chemin de l'endpoint de vérification de santé
METRICSROOT:str = os.getenv('METRICSROOT', 'metrics') # Chemin de l'endpoint des métriques

# Vérification que les intervalles sont des entiers positifs
if not check_positive(INTERVAL_HEALTH_CHECK) or not check_positive(INTERVAL_METRICS_CHECK):
raise ValueError("INTERVAL_HEALTH_CHECK et INTERVAL_METRICS_CHECK doivent être des entiers positifs")

# Lecture de la variable WHITELIST_AGENT et conversion en un ensemble
whitelist_agent_str:str = os.getenv('WHITELIST_AGENT', '')
if whitelist_agent_str:
WHITELIST_AGENT = set(whitelist_agent_str.split(','))
else:
raise ValueError("WHITELIST_AGENT est vide")

# BLACKLIST_AGENT reste un ensemble vide par défaut
BLACKLIST_AGENT = set()
TIMESTAMP = datetime.now().isoformat() # Date et heure actuelles au format ISO

# Fonction pour sauvegarder les données dans un fichier
def save_metrics(ip, info):
"""
Sauvegarde les métriques pour un agent dans le fichier JSON.

Args:
- ip (str): L'adresse IP de l'agent.
- info (dict): Les informations (métriques) à sauvegarder.
"""
try:
with open(METRICS_FILE, 'r') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
metrics = json.load(f)
except FileNotFoundError:
print(f'fichier {METRICS_FILE} non existant')
metrics = {}
except json.JSONDecodeError:
print(f"Le fichier {METRICS_FILE} est vide ou corrompu. Réinitialisation.")
metrics = {}

def save_metrics(metrics):
# Ajouter l'IP si elle n'existe pas encore dans le fichier
if ip not in metrics:
metrics[ip] = []

# Ajouter les nouvelles informations pour l'IP
metrics[ip].append(info)

# Sauvegarder les nouvelles données dans le fichier
try:
with open(METRICS_FILE, 'w') as f:
json.dump(metrics, f, indent=4)
except FileNotFoundError:
print(f'fichier {METRICS_FILE} non existant')
except json.JSONDecodeError:
print(f"Le fichier {METRICS_FILE} est vide ou corrompu. Impossible de sauvegarder")

# Fonction pour envoyer une requête de vérification de santé à un agent
def get_healthcheck(agent_url: str)->int:
"""
Sauvegarde les métriques dans un fichier JSON.
Envoie une requête GET à l'agent pour vérifier sa santé.

Args:
- agent_url (str): L'URL de l'agent à interroger.

Returns:
- int: Le code de statut HTTP retourné par l'agent.
"""
with open(METRICS_FILE, 'w') as f:
json.dump(metrics, f, indent=4)
try:
res = requests.get(f'http://{agent_url}/{HEALTHROOT}', timeout=5) # Timeout de 5 secondes
print(f'health_check status code: {res.status_code} from agent: {agent_url}')
return res.status_code
except requests.exceptions.RequestException as e:
print(f"Erreur lors de la requête de santé pour {agent_url}: {str(e)}")
return 500

@app.route('/getmet', methods=['GET'])
def get_metrics():
# Fonction pour récupérer les métriques d'un agent
def get_metrics(agent_url):
"""
Envoie une requête à l'agent pour récupérer les métriques.
Récupère les métriques d'un agent et les sauvegarde dans le fichier JSON.

Args:
- agent_url (str): L'URL de l'agent à interroger.
"""
try:
response = requests.get(AGENT_URL)
response = requests.get(f'http://{agent_url}/{METRICSROOT}')
if response.status_code == 200:
data = response.json()

# Ajoute un horodatage aux métriques
timestamp = datetime.now().isoformat()
metrics = load_metrics()
metrics[timestamp] = data
save_metrics(metrics)

return jsonify({"status": "success", "metrics": data, "timestamp": timestamp}), 200
else:
return jsonify({"error": f"Échec de la requête à l'agent. Code HTTP : {response.status_code}"}), 500
except requests.RequestException as e:
return jsonify({"error": f"Erreur lors de la requête à l'agent : {str(e)}"}), 500
@app.route('/')
def index():
return "Collecteur de métriques en ligne!"
if not response.json():
return print("Error, No data received", 400)
else:
save_metrics(agent_url, response.json())
return print(f"get metrics success, data received and saved from agent: {agent_url}", 200)
except requests.exceptions.RequestException as e:
return print({"error": str(e)}), 500

# Fonction pour vérifier la santé de tous les agents dans la liste blanche
def health_check():
"""
Effectue une vérification de santé pour chaque agent dans la liste blanche (WHITELIST_AGENT).
Si un agent échoue, il est ajouté à la liste noire (BLACKLIST_AGENT).
"""
for agent in WHITELIST_AGENT:
res = get_healthcheck(str(agent))
log = {"Health_check_status": res, "timestamp": TIMESTAMP.split('.')[0]}
if res != 200 and agent not in BLACKLIST_AGENT:
BLACKLIST_AGENT.add(agent)
elif res == 200 and agent in BLACKLIST_AGENT:
BLACKLIST_AGENT.remove(agent)

save_metrics(agent, log)

# Fonction pour récupérer les métriques de tous les agents dans la liste blanche
def metrics_check():
"""
Récupère les métriques pour chaque agent dans la liste blanche (WHITELIST_AGENT),
à condition que l'agent ne soit pas dans la liste noire.
"""
for agent in WHITELIST_AGENT:
if agent not in BLACKLIST_AGENT:
get_metrics(agent)

# Tâche asynchrone pour effectuer des vérifications de santé à intervalles réguliers
async def interval_health_check():
while True:
health_check()
await asyncio.sleep(INTERVAL_HEALTH_CHECK) # Attente avant la prochaine vérification

# Tâche asynchrone pour récupérer les métriques à intervalles réguliers
async def interval_metrics_check():
while True:
metrics_check()
await asyncio.sleep(INTERVAL_METRICS_CHECK) # Attente avant la prochaine récupération

# Fonction principale asynchrone qui exécute les deux tâches en parallèle
async def main():
await asyncio.gather(interval_health_check(), interval_metrics_check())

# Exécution du programme
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
asyncio.run(main())
1 change: 1 addition & 0 deletions metrics_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@