Aller au contenu principal

SDK et CLI du Navin Manager

Ce guide montre comment appeler l'API du Navin Manager (navin-manager), qui installe et exploite l'infrastructure de la plateforme : hôtes, clusters, services, haute disponibilité, sauvegardes, restaurations et reprise après sinistre. Il couvre le client du manager de chaque SDK et la ligne de commande enterprise-ai manager. L'API est décrite par navin-manager/docs/api/manager-openapi.json, écrit depuis le serveur par tools/manager/openapi.py.

English version: manager.md

Vue d'ensemble​

Les clients du manager sont générés depuis le document OpenAPI par tools/manager/gen_sdks.py : une méthode par opération sous /api/v1, regroupée par tag (269 opérations dans 31 groupes), plus des alias du contrat comme hosts.preflight, services.rollback, ha.test_failover, backups.create_plan, restores.execute et dr.request_failover. Un test de couverture dans chaque SDK échoue dès qu'une opération du document n'a pas de méthode.

SDKPoint d'entréeTable généréeTest de couverture
Pythonenterprise_ai.manager.ManagerClient, AsyncManagerClientsdks/python/src/enterprise_ai/manager/_generated.pysdks/python/tests/test_manager.py
TypeScriptManagerClient de @enterprise-ai/sdksdks/typescript/packages/sdk/src/manager/generated/operations.tssdks/typescript/packages/sdk/test/manager.test.ts
Gopaquet manager du module du SDK Gosdks/go/manager/operations_gen.gosdks/go/manager/manager_test.go
Javaio.enterpriseai.manager.ManagerClientsdks/java/enterprise-ai-java/src/main/java/io/enterpriseai/manager/ManagerGroups.javala suite de tests Java

Le client du manager n'est pas le sous-client platform_manager du SDK du Control Plane : celui-ci atteint l'espace Platform Manager de la console par le Control Plane, pour un platform_admin.

Prérequis et compatibilité​

Le client du manager est livré dans chaque SDK et a les mêmes prérequis : Python 3.10+, Node.js 18+, Go 1.26+, Java 17+ (vue d'ensemble des SDK). Il cible l'API v1 du manager (/api/v1). Installez le SDK comme décrit dans son guide ; aucun paquet supplémentaire n'est nécessaire.

Authentification et délégation​

  1. Créez un jeton d'API du manager. Dans la console du manager, Profil > Jetons d'API, créez un jeton avec la plus petite liste de permissions. Le jeton a la forme nmt_<12 hex>_<secret> ; chaque SDK refuse un autre format avant tout appel.
  2. Rangez le jeton dans un fichier lisible par son seul propriétaire (mode 0600) et donnez son chemin (NM_MANAGER_TOKEN_FILE, token_file, tokenFile), ou passez-le depuis un coffre de secrets. Aucun SDK et aucune commande de la CLI ne l'affiche.
  3. Agissez pour un utilisateur quand un service appelle au nom d'une personne. L'option on_behalf_of (onBehalfOf en TypeScript et en Java, WithOnBehalfOf en Go) envoie X-NM-On-Behalf-Of: <utilisateur>. Le manager ne l'accepte que d'un jeton dont la liste explicite de permissions contient auth:delegate : la requête porte alors l'intersection des permissions du jeton et de l'utilisateur, et chaque événement d'audit enregistre l'acteur (le jeton de service) ainsi que details.on_behalf_of et details.delegation. Tout autre appelant reçoit HTTP 403 on_behalf_of_forbidden ; un utilisateur inconnu ou désactivé reçoit 403 on_behalf_of_unknown.
attention

Les identifiants du Control Plane (EAI_*) ne sont jamais envoyés au manager : les clients du manager ne lisent que leurs propres réglages.

Configuration du client​

RéglagePython (ManagerConfig)TypeScript (ManagerClientOptions)Go (manager.With...)Java (ManagerClient.Builder)Variable d'environnement
URL du manager (https, ou http sur localhost)base_url: strbaseUrl: stringWithBaseURL(string)baseUrl(String)NM_MANAGER_URL
Jetontoken: str | Nonetoken: string | () => string | Promise<string>WithToken(string)token(String)NM_MANAGER_TOKEN
Fichier du jetontoken_file: str | NoneaucunaucuntokenFile(Path)NM_MANAGER_TOKEN_FILE
Autorités de certification (PEM)ca_bundle: str | Noneconfiance de l'environnement (NODE_EXTRA_CA_CERTS)WithCABundle([]byte)caBundle(Path)NM_MANAGER_CA_BUNDLE
Délégationon_behalf_of: str | NoneonBehalfOf: stringWithOnBehalfOf(string)onBehalfOf(String)NM_MANAGER_ON_BEHALF_OF
Langue (en, fr)locale: str | NonelocaleWithLocale(string)locale(Locale)NM_MANAGER_LOCALE
Délaitimeout: float = 30.0timeoutMs (60000 par défaut)WithHTTPClient(...)timeout(Duration) (30 s), connectTimeout(Duration) (10 s)aucune
Reprisesretries: int = 2retries (2 par défaut)WithRetries(int) (2), WithRetryBaseDelay (250 ms)retries(int) (2), retryBaseDelay(Duration) (250 ms)aucune
En-têtes supplémentairesheaders: dict[str, str]headersWithHeader(name, value)header(name, value)aucune
Client HTTPhttp_client=fetchWithHTTPClient(*http.Client)httpClient(HttpClient)aucune

Les variables d'environnement sont lues par ManagerConfig.from_env() (Python, utilisé aussi quand aucun config n'est donné) et par ManagerClient.Builder.fromEnv(System.getenv()) ou ManagerClient.fromEnv() (Java). TypeScript et Go prennent les valeurs depuis votre code.

Démarrage rapide et groupes de ressources​

Python​

from enterprise_ai.manager import ManagerClient

with ManagerClient("<manager-url>", token=TOKEN, ca_bundle=None, http_client=HTTP) as nm:
check = nm.hosts.preflight({"cluster_id": "c1", "target": {"host": "10.0.0.5"}, "private_key": "ref",
"fqdn": "<host-fqdn>", "expected_ip": "10.0.0.5"})
op = nm.services.action("c1", "kafka", {"action": "restart"})
status = nm.operations.get_operation(op["operation_id"])
for item in nm.operations.list_operations("c1", limit=50):
print(item["id"])
as_alice = nm.on_behalf_of("alice")
as_alice.restores.plan({"artifact_id": "a1", "environment": "staging"})

Ce qui se passe : le client vérifie le format du jeton, ouvre un pool de connexions, lance le contrôle préalable d'un nouvel hôte, redémarre un service, lit l'opération qui en résulte, parcourt chaque page des opérations du cluster, puis planifie une restauration au nom d'alice. AsyncManagerClient a les mêmes méthodes, chacune renvoyant un awaitable. nm.operation("<operationId>") renvoie la méthode liée de n'importe quel identifiant d'opération du document.

TypeScript​

import { ManagerClient, collect, paginate } from "@enterprise-ai/sdk";

const nm = new ManagerClient({ baseUrl: "<manager-url>", token, onBehalfOf: "alice" });
const op = await nm.services.action("c1", "kafka", { action: "restart" });
const ops = await collect(paginate((q) => nm.operations.listOperations("c1", q), { limit: 50 }));

Go​

nm, err := manager.New(manager.WithBaseURL("<manager-url>"), manager.WithToken(token),
manager.WithCABundle(pem), manager.WithOnBehalfOf("alice"))
raw, err := nm.Services.Action(ctx, "c1", "kafka", map[string]any{"action": "restart"})

Java​

ManagerClient nm = ManagerClient.builder()
.baseUrl("<manager-url>")
.tokenFile(Path.of("/run/secrets/manager.token"))
.caBundle(Path.of("/etc/pki/manager-ca.pem"))
.build();
JsonNode op = nm.services().action("c1", "kafka", Map.of("action", "restart"));
for (JsonNode item : nm.operations().listOperations("c1").autoPaging()) {
System.out.println(item.path("id").asText());
}
ManagerClient asAlice = nm.onBehalfOf("alice");

Groupes de ressources​

Chaque groupe existe dans les quatre SDK (Python nm.hosts, TypeScript nm.hosts, Go nm.Hosts, Java nm.hosts()). Le nombre est celui des opérations du document dans le groupe.

GroupeOpérationsGroupeOpérationsGroupeOpérations
alerts10configs12node_security22
audit3connections7operations7
auth2consoles4pki3
auth_config11hosts13services22
availability6hub5settings2
backup31kubernetes19stack3
blueprint3lifecycle29system3
clusters10mcp12telemetry1
me3mcp_runtime4users10
meta1mirror2vault7
network2

Les groupes du contrat backups, restores, ha et dr ne contiennent que des alias des opérations ci-dessus (par exemple restores.plan, ha.test_failover). Chaque méthode, avec ses paramètres et sa route, est dans la référence générée : Python, TypeScript, Go, Java.

Erreurs​

Les erreurs gardent l'enveloppe du manager {"error": {"code", "message", "message_i18n", "details"}} et sont les erreurs typées de chaque SDK : sous-classes de EaiError en Python (messages contient message_i18n), sous-classes de EaiError en TypeScript (messageI18n), *eai.APIError en Go (MessageI18n), sous-classes de EaiException en Java. Les codes propres au manager comprennent on_behalf_of_forbidden, on_behalf_of_unknown (403) et les codes d'approbation, de verrou et de conflit des opérations (409, 423, 428).

Reprises et délais​

Le manager ne dédoublonne pas les appels POST par Idempotency-Key, donc les clients ne reprennent jamais une opération qui a pu s'exécuter :

SDKAppels reprisÉchecs repris
PythonGET, PUT, DELETE ; toute méthode sur HTTP 429erreurs réseau, 429, 500, 502, 503, 504
TypeScriptGET, PUT, DELETE (les autres méthodes utilisent noRetry)erreurs réseau, délais dépassés, 429, 5xx
GoGET, PUT, DELETEerreurs réseau, 408, 429, 502, 503, 504
JavaGET, PUT, DELETEerreurs d'entrée-sortie, 408, 429, 502, 503, 504

Pagination et opérations longues​

Les opérations de liste prennent limit et cursor et renvoient la page {items, next_cursor} de chaque SDK : Page en Python, Page<Doc> en TypeScript (parcourue avec paginate et collect), json.RawMessage en Go (avec eai.WithLimit et eai.WithCursor), Page<JsonNode> en Java (autoPaging()). Les actions sur les services, les hôtes et les sauvegardes répondent par une opération : lisez-la avec operations.get_operation(id) (getOperation, GetOperation), et ses tâches et journaux avec les autres méthodes du groupe operations.

Opérations exemptées​

Les parcours navigateur et MFA ne sont pas enveloppés : ils exigent le cookie de session et le jeton CSRF d'une personne, jamais un jeton de service. La liste avec ses raisons est EXEMPT (Python), MANAGER_EXEMPT (TypeScript), manager.Exempt (Go) et ManagerOperations.EXEMPT (Java) : auth/login, auth/logout, auth/setup, auth/password/change, auth/oidc/{provider_id}/start et callback, auth/mfa/verify, auth/mfa/enroll/start et confirm, auth/mfa/setup, auth/mfa/confirm, DELETE auth/mfa, POST me/tokens (un jeton ne peut pas créer de jetons) et PATCH me (profil modifié depuis la session).

Tests​

Chaque client du manager accepte une couche HTTP injectée, exactement comme le client du Control Plane : Python http_client=httpx.Client(transport=httpx.MockTransport(handler)), TypeScript fetch, Go WithHTTPClient avec un serveur httptest, Java httpClient ou un serveur local. Les exemples de cette page sont exécutés par sdks/python/tests/test_manager.py contre un manager simulé.

Référence de la CLI​

Configuration, de la plus prioritaire à la moins prioritaire : options (--manager-url, --ca-bundle, --token-file, --on-behalf-of, --manager-config), variables d'environnement (NM_MANAGER_URL, NM_MANAGER_CA_BUNDLE, NM_MANAGER_TOKEN_FILE ou NM_MANAGER_TOKEN, NM_MANAGER_ON_BEHALF_OF, NM_MANAGER_CONFIG), puis le fichier JSON ~/.config/enterprise-ai/manager.json avec les clés url, ca_bundle, token_file, on_behalf_of, locale. Une clé token dans ce fichier est refusée (code 7). --json affiche la réponse JSON brute ; --body FICHIER (JSON ou YAML, - pour l'entrée standard) donne le corps de la requête, les options s'y ajoutent. L'aide fonctionne hors ligne : enterprise-ai manager --help --lang fr.

CommandeRoute du manager
host add --body F [--cluster C]POST /api/v1/hosts/bootstrap/plan (plan de l'ajout, rien n'est exécuté)
host preflight --body FPOST /api/v1/hosts/preflight
host bootstrap --body FPOST /api/v1/hosts/bootstrap (clé d'hôte stricte, SSH temporaire)
host enroll --cluster C --fqdn N --fingerprint SHA256 [--ttl S]POST /api/v1/hosts/enroll
host list [--cluster C]GET /api/v1/hosts
host show HGET /api/v1/hosts/{host_id}
host revoke H [--reason R]POST /api/v1/hosts/{host_id}/revoke
host diagnose HGET /api/v1/agents/nodes/{host_id}, GET /api/v1/hosts/{host_id}/inventory, GET /api/v1/hosts/{host_id}/preflight
cluster import C --api-server URL --kubeconfig F [--namespace N]POST /api/v1/clusters/{cluster_id}/kubernetes
cluster listGET /api/v1/clusters
cluster nodes CGET /api/v1/clusters/{cluster_id}/hosts
cluster pools CGET /api/v1/clusters/{cluster_id}/topology, hôtes regroupés par baie (domaine de panne) ; le manager n'a pas de route de pool de nœuds
service install C S [--body F] [--no-start]POST /api/v1/clusters/{cluster_id}/services
service configure C S --body F [--note N]PUT /api/v1/clusters/{cluster_id}/services/{service}/config
service start|stop|restart C S [--approval A]POST /api/v1/clusters/{cluster_id}/services/{service}/actions
service upgrade C S [--approval A] [--canary-host H]POST /api/v1/clusters/{cluster_id}/services/{service}/upgrade
service rollback C UPGRADE [--approval A]POST /api/v1/clusters/{cluster_id}/upgrades/{upgrade_id}/rollback
service status C SGET /api/v1/clusters/{cluster_id}/services/{service}
ha create --body FPOST /api/v1/ha-groups
ha status GGET /api/v1/ha-groups/{group_id}
ha test-failover GPOST /api/v1/ha-groups/{group_id}/test-failover
backup plan --body FPOST /api/v1/backup-plans
backup run P --location L [--kind K]POST /api/v1/backup-plans/{plan_id}/backups
backup verify BPOST /api/v1/backups/{backup_id}/verify
backup listGET /api/v1/backups
restore plan --artifact A --environment EPOST /api/v1/restores/plan
restore approve PPOST /api/v1/restores/{plan_id}/approve (séparation des tâches imposée par le manager)
restore execute PPOST /api/v1/restores/{plan_id}/execute
restore verify PGET /api/v1/restores/{plan_id} (état, approbateurs, vérification)
dr plan --body FPOST /api/v1/dr-plans
dr exercise --plan PPOST /api/v1/dr-exercises
dr failover-request --plan PPOST /api/v1/dr-failovers/request (demande seulement : approbation requise)

Codes de sortie : 0 succès ; 1 entrée invalide (contrôle local, HTTP 400 ou 422) ; 2 erreur d'utilisation ; 3 authentification ou permission refusée (HTTP 401 ou 403) ; 4 introuvable (HTTP 404) ; 5 conflit, verrou ou approbation requise (HTTP 409, 423 ou 428) ; 6 erreur du serveur ou du réseau ; 7 erreur de configuration.

Exemples​

Ces lignes sont exécutées par sdks/python/tests/test_manager.py contre un manager simulé.

export NM_MANAGER_URL=<manager-url> NM_MANAGER_TOKEN_FILE=~/.config/enterprise-ai/manager.token
enterprise-ai manager host preflight --cluster c1 --body host.json --json
enterprise-ai manager host bootstrap --cluster c1 --body host.json
enterprise-ai manager host enroll --cluster c1 --fqdn <host-fqdn> --fingerprint SHA256:abc
enterprise-ai manager host list --cluster c1
enterprise-ai manager host diagnose h1 --json
enterprise-ai manager host revoke h1 --reason "key compromised"
enterprise-ai manager cluster import c1 --api-server <api-server-url> --kubeconfig kubeconfig
enterprise-ai manager cluster pools c1 --json
enterprise-ai manager service install c1 kafka
enterprise-ai manager service configure c1 kafka --body kafka.json --note "retention 7 days"
enterprise-ai manager service restart c1 kafka
enterprise-ai manager service upgrade c1 kafka --approval ap1
enterprise-ai manager service rollback c1 up1 --approval ap2
enterprise-ai manager service status c1 kafka --json
enterprise-ai manager ha create --body ha.json
enterprise-ai manager ha test-failover g1
enterprise-ai manager backup plan --body backup-plan.json
enterprise-ai manager backup run p1 --location s3://backups/pg/2026-10-30
enterprise-ai manager backup verify b1
enterprise-ai manager backup list --json
enterprise-ai manager restore plan --artifact a1 --environment staging
enterprise-ai manager restore approve r1 --on-behalf-of alice
enterprise-ai manager restore execute r1
enterprise-ai manager restore verify r1 --json
enterprise-ai manager dr plan --body dr-plan.json
enterprise-ai manager dr exercise --plan d1
enterprise-ai manager dr failover-request --plan d1

Référence​