Aller au contenu
6 minutes

Auditer la nomenclature des VM et leurs catégories par l’API v4

Une regex pour la règle de nommage, deux listes v4 pour les VM et les catégories, un statut par VM et un CSV d’écarts. Le script constate, l’humain décide de ce qu’on corrige.

Auditer la nomenclature des VM et leurs catégories par l'API v4

Une convention de nommage n’existe que si quelqu’un vérifie qu’elle est tenue. Un audit par API compare chaque nom de VM à la règle, puis à ses catégories, et sort la liste des écarts, sans rien corriger tout seul.

Contexte

Les catégories rangent les VM (le concept est détaillé dans ce billet). Quand elles sont posées par des playbooks qui lisent le nom, une règle de nommage floue donne des tags faux ou absents. Le rattrapage par phases et les défauts de playbooks sont racontés ici ; ce billet porte sur l’audit lui-même.

Écrire la règle en regex

Une regex à groupes nommés décrit la convention : les groupes entité et environnement donnent directement les valeurs de catégorie attendues.

import re

REGLE = re.compile(
    r"^(?P<entite>[a-z]{2})(?P<env>\d{2})(?P<fonction>[a-z]+)(?P<site>[a-z])(?P<instance>\d{3})$"
)
ENV = {"31": "Production", "32": "Preproduction", "33": "Recette"}  # table de la convention

Trois leçons terrain. D’abord, fixer la position : une regex de playbook en ^.{0,2}<code> laissait le code tomber au mauvais endroit, elle a été durcie en ^.{2}<code>.+[0-9]{3}$. Ensuite, la centaine du numéro d’instance (xy31webb203) n’est pas un environnement : c’est un numéro d’instance ou de réplica. Enfin, l’environnement qui fait foi reste la catégorie posée ; le nom n’est qu’un indice fort.

Collecter par l’API v4

L’audit d’origine passait par l’API v3 ; la collecte v4 ci-dessous, écrite d’après la doc, n’a pas été rejouée sur le terrain. Deux listes suffisent. Côté VM, GET /api/vmm/v4.0/ahv/config/vms (chemin d’un exemple nutanix.dev) : d’après la doc de list_vms dans le SDK, categories fait partie des champs de $select, pas de ceux de $filter. Chaque entrée est un CategoryReference qui ne porte qu’un extId. Côté catégories, la liste v4 (GET /api/prism/v4.0/config/categories, utilisée sur le terrain lors d’un renommage) donne extId, key et value : on en fait un dictionnaire. On indexe tout par extId, jamais par nom (les homonymes existent).

import csv
import os

import requests

PC = os.environ["PC_HOST"]
AUTH = (os.environ["PC_USER"], os.environ["PC_PASSWORD"])
VERIFY = os.environ.get("PC_CA_BUNDLE") or True  # bundle CA ; variable vide = vérification par défaut


def lister(chemin, select):
    page, sortie = 0, []
    while True:
        r = requests.get(
            f"https://{PC}:9440/api/{chemin}?$page={page}&$limit=100&$select={select}",
            auth=AUTH, verify=VERIFY, timeout=30,
        )
        r.raise_for_status()
        corps = r.json()
        data = corps.get("data") or []
        sortie += data
        if not data or len(sortie) >= corps["metadata"]["totalAvailableResults"]:
            return sortie
        page += 1


CATS = {c["extId"]: (c["key"], c["value"])
        for c in lister("prism/v4.0/config/categories", "extId,key,value")}
VMS = lister("vmm/v4.0/ahv/config/vms", "extId,name,categories")

La pagination ($page à index zéro, $limit à 100 au plus) et totalAvailableResults sont détaillés dans le billet OData.

Classer chaque VM

def classer(vm):
    nom = vm["name"].lower()
    if nom.endswith("_old"):
        return "EXCLUE", ""
    m = REGLE.match(nom)
    if not m or m["env"] not in ENV:
        return "NON_DERIVABLE", ""
    attendu = ENV[m["env"]]
    poses = {CATS.get(c["extId"], (None, None)) for c in vm.get("categories") or []}
    actuel = {v for k, v in poses if k == "Env"}
    if not actuel:
        return "A_TAGUER", attendu
    if attendu not in actuel:
        return "CONFLIT", f"tag {sorted(actuel)}, nom {attendu}"
    return "OK", ""


with open("audit_nomenclature.csv", "w", newline="", encoding="utf-8-sig") as f:
    w = csv.writer(f, delimiter=";")  # s'ouvre tel quel dans un tableur français
    w.writerow(["extId", "nom", "statut", "detail"])
    for vm in VMS:
        w.writerow([vm["extId"], vm["name"], *classer(vm)])

Le statut CONFLIT est le plus précieux : le nom dit une chose, le tag une autre, et seul un humain sait lequel a raison. Au premier audit terrain, il n’y en avait aucun sur l’entité et l’environnement ; le vrai sujet était les VM sans tag.

Lire les non dérivables

Ne les rangez pas d’office en « traitement manuel ». Sur le terrain, environ 80 % des noms non dérivables suivaient une variante (une lettre puis le code d’environnement), avec un code valide en position 2-3 dans tous les cas. L’environnement devenait dérivable ; l’entité, non, faute de confirmation de la convention. Une proposition a été faite, sans que rien ne soit tagué avant décision. Le reste, noms libres et copies, part en revue humaine.

Dernier contrôle utile : les VM créées récemment, conformes mais sans tag. Le terrain n’en a trouvé aucune après correction des playbooks, et c’est ce qui a montré qu’ils fonctionnaient. Le champ createTime figure parmi les champs de $select.

Ce qu’il faut retenir

  • La règle de nommage s’écrit en regex à groupes nommés, position du code fixée.
  • categories se lit via $select sur la liste v4 des VM, puis se traduit par un dictionnaire extId vers clé:valeur.
  • Cinq statuts : conforme, à taguer, conflit, non dérivable, exclue. Le conflit se tranche à la main.
  • L’audit ne corrige rien ; l’application est une phase séparée, après revue.
  • Décomposez les non dérivables : une variante peut rester partiellement dérivable.

In English

A regex for the naming rule, two v4 lists for VMs and categories, one status per VM and a CSV of discrepancies. The script observes, the human decides what gets fixed. The v4 VM list accepts categories in $select but not in $filter, and the regex must pin the position of the environment code, since the hundreds digit of the instance number is not one.

Sources & vérification

Testé sur : Prism Central de production, août 2026 (audit d'origine via l'API v3)

Comment les billets sont vérifiés et corrigés