Source code for utils.data_generator

"""
Module de génération de données factices avec Faker.
Génère des données réalistes pour les utilisateurs et les machines virtuelles.
"""

import random
import unicodedata
from typing import List, Dict, Any
from faker import Faker
from faker.providers import internet, company, lorem, date_time

from utils.logging_config import get_logger

# Logger pour ce module
logger = get_logger(__name__)

# Initialisation de Faker avec la locale française
fake = Faker("fr_FR")
fake.add_provider(internet)
fake.add_provider(company)
fake.add_provider(lorem)
fake.add_provider(date_time)


[docs] class VMDataGenerator: """Générateur de données pour les machines virtuelles.""" # Systèmes d'exploitation réalistes OPERATING_SYSTEMS = [ "Ubuntu 22.04 LTS", "Ubuntu 20.04 LTS", "CentOS 8", "Red Hat Enterprise Linux 8", "Windows Server 2022", "Windows Server 2019", "Debian 11", "SUSE Linux Enterprise Server 15", "AlmaLinux 8", "Rocky Linux 8", "Fedora 38", "openSUSE Leap 15.4", ] # Statuts possibles des VMs VM_STATUSES = ["running", "stopped", "paused", "provisioning", "deleting"] # Probabilités de statut (plus réalistes) STATUS_PROBABILITIES = { "running": 0.6, # 60% des VMs sont en cours d'exécution "stopped": 0.25, # 25% sont arrêtées "paused": 0.05, # 5% sont en pause "provisioning": 0.05, # 5% sont en cours de création "deleting": 0.05, # 5% sont en cours de suppression }
[docs] @classmethod def generate_vm_name(cls) -> str: """Génère un nom réaliste pour une VM.""" prefixes = [ "web", "db", "app", "api", "cache", "monitor", "backup", "test", "dev", "prod", ] suffix = fake.word().capitalize() return f"{random.choice(prefixes)}-{suffix}"
[docs] @classmethod def generate_cpu_cores(cls) -> int: """Génère un nombre réaliste de cœurs CPU.""" # Distribution réaliste : plus de VMs avec peu de cœurs weights = [0.3, 0.4, 0.2, 0.1] # 1-2, 4, 8, 16+ cœurs ranges = [(1, 2), (4, 4), (8, 8), (16, 32)] range_choice = random.choices(ranges, weights=weights)[0] return random.randint(range_choice[0], range_choice[1])
[docs] @classmethod def generate_ram_gb(cls) -> int: """Génère une quantité réaliste de RAM.""" # Distribution réaliste : plus de VMs avec peu de RAM weights = [0.2, 0.3, 0.3, 0.2] # 2-4, 8, 16, 32+ GB ranges = [(2, 4), (8, 8), (16, 16), (32, 128)] range_choice = random.choices(ranges, weights=weights)[0] return random.randint(range_choice[0], range_choice[1])
[docs] @classmethod def generate_disk_gb(cls) -> int: """Génère une taille réaliste de disque.""" # Distribution réaliste : plus de VMs avec des disques moyens weights = [0.1, 0.4, 0.3, 0.2] # 20-50, 100, 200, 500+ GB ranges = [(20, 50), (100, 100), (200, 200), (500, 2000)] range_choice = random.choices(ranges, weights=weights)[0] return random.randint(range_choice[0], range_choice[1])
[docs] @classmethod def generate_status(cls) -> str: """Génère un statut réaliste basé sur les probabilités.""" return random.choices( list(cls.STATUS_PROBABILITIES.keys()), weights=list(cls.STATUS_PROBABILITIES.values()), )[0]
[docs] @classmethod def generate_vm(cls, user_id: int, vm_id: int) -> Dict[str, Any]: """Génère une VM complète avec des données réalistes.""" # Date de création dans les 6 derniers mois created_at = fake.date_time_between(start_date="-6M", end_date="now") vm_data = { "id": vm_id, "user_id": user_id, "name": cls.generate_vm_name(), "operating_system": random.choice(cls.OPERATING_SYSTEMS), "cpu_cores": cls.generate_cpu_cores(), "ram_gb": cls.generate_ram_gb(), "disk_gb": cls.generate_disk_gb(), "status": cls.generate_status(), "created_at": created_at, } logger.debug("VM générée", vm_id=vm_id, user_id=user_id, name=vm_data["name"]) return vm_data
[docs] class UserDataGenerator: """Générateur de données pour les utilisateurs.""" # Domaines d'entreprise réalistes COMPANY_DOMAINS = [ "gmail.com", "outlook.com", "yahoo.com", "hotmail.com", "company.com", "entreprise.fr", "corp.com", "business.org", "tech.io", "startup.com", "innovation.fr", "digital.net", ]
[docs] @classmethod def generate_email(cls, name: str) -> str: """Génère un email réaliste basé sur le nom.""" # Nettoyer le nom pour l'email : supprimer les accents et caractères spéciaux # Normaliser les caractères Unicode normalized = unicodedata.normalize("NFD", name.lower()) # Supprimer les caractères diacritiques (accents) clean_name = "".join(c for c in normalized if unicodedata.category(c) != "Mn") # Remplacer les espaces par des points et supprimer autres caractères spéciaux clean_name = clean_name.replace(" ", ".").replace("-", "").replace("'", "") # Garder seulement les caractères alphanumériques et les points clean_name = "".join(c for c in clean_name if c.isalnum() or c == ".") domain = random.choice(cls.COMPANY_DOMAINS) return f"{clean_name}@{domain}"
[docs] @classmethod def generate_user(cls, user_id: int) -> Dict[str, Any]: """Génère un utilisateur complet avec des données réalistes.""" # Générer un nom français réaliste name = fake.name() # Date de création dans les 12 derniers mois created_at = fake.date_time_between(start_date="-12M", end_date="now") user_data = { "id": user_id, "name": name, "email": cls.generate_email(name), "created_at": created_at, "vms": [], # Les VMs seront ajoutées séparément } logger.debug( "Utilisateur généré", user_id=user_id, name=name, email=user_data["email"] ) return user_data
[docs] class DataGenerator: """Générateur principal pour créer des datasets complets."""
[docs] @classmethod def generate_users_with_vms( cls, user_count: int = 50, vm_per_user_range: tuple = (0, 5) ) -> List[Dict[str, Any]]: """ Génère un dataset complet d'utilisateurs avec leurs VMs. Args: user_count: Nombre d'utilisateurs à générer vm_per_user_range: Tuple (min, max) du nombre de VMs par utilisateur Returns: Liste des utilisateurs avec leurs VMs associées """ logger.info( "Génération du dataset", user_count=user_count, vm_range=vm_per_user_range ) users = [] vm_counter = 1 for user_id in range(1, user_count + 1): # Générer l'utilisateur user = UserDataGenerator.generate_user(user_id) # Générer les VMs pour cet utilisateur vm_count = random.randint(vm_per_user_range[0], vm_per_user_range[1]) user_vms = [] for _ in range(vm_count): vm = VMDataGenerator.generate_vm(user_id, vm_counter) user_vms.append(vm) vm_counter += 1 user["vms"] = user_vms users.append(user) logger.debug( "Utilisateur généré avec VMs", user_id=user_id, vm_count=vm_count, user_name=user["name"], ) total_vms = sum(len(user["vms"]) for user in users) logger.info( "Dataset généré avec succès", total_users=len(users), total_vms=total_vms, avg_vms_per_user=total_vms / len(users) if users else 0, ) return users
[docs] @classmethod def generate_vms_only( cls, vm_count: int, user_ids: List[int] ) -> List[Dict[str, Any]]: """ Génère uniquement des VMs pour des utilisateurs existants. Args: vm_count: Nombre de VMs à générer user_ids: Liste des IDs d'utilisateurs existants Returns: Liste des VMs générées """ logger.info( "Génération de VMs", vm_count=vm_count, available_users=len(user_ids) ) vms = [] for vm_id in range(1, vm_count + 1): user_id = random.choice(user_ids) vm = VMDataGenerator.generate_vm(user_id, vm_id) vms.append(vm) logger.info("VMs générées avec succès", count=len(vms)) return vms