class

Beryl::Providers::Ovh

Inherits Beryl::ComputeProvider < Beryl::DnsProvider < Beryl::Provider < Reference < Object

Provider OVHcloud pour beryl. Encapsule la détection des credentials (via Beryl::CLI::Credentials.ovh_client) et le listing des clés SSH enregistrées dans le compte.

Le client OVH est injectable via le constructeur pour les tests. En production, on laisse le défaut nil et client résout à la demande via Credentials.ovh_client.

Constructors

new(injected_client : OvhApi::Client | Nil = nil)
Source

Class methods

host_ipv6(block : String) : String

2001:41d0:0364:3e00:0000:…/56 → 2001:41d0:364:3e00::1 : forme canonique, ::1 quand le bloc se termine par :: (le :: nu est l'anycast routeur, jamais le serveur). Pur.

Source
period_months(iso : String) : Int32 | Nil

Nombre de mois d'une durée ISO 8601 de facturation (P1M → 1, P1Y → 12, P6M → 6, P1Y6M → 18). nil si illisible ou nulle.

Source

Instance methods

attach_dedicated_server(vrack : String, service_name : String) : String

Rattache le serveur au vRack (modèle interface vRack 2.0). Retourne l'id de la task. Lève si le serveur n'a pas d'interface vRack.

Source
available?

Vrai si les credentials nécessaires sont disponibles (variables d'env, fichiers de config, agent local…). Ne lève jamais : un provider « indisponible » est simplement sauté par beryl init.

Source
boot_from_disk(resource_id : String) : String

Reboot depuis le disque (inverse de request_rescue). Même signature de retour (task id).

Source
bootstrap_credentials_if_needed(env : Hash(String, String), force_regen : Bool = false, interactive : Bool = true) : Hash(String, String)

Génère la consumer key OVH si absente (ou si force_regen). Utilise POST /auth/credential du shard ovh-api 0.4.0 avec la liste exacte des access rules de required_access_rules.

Flux utilisateur :

  1. Requête API → retourne une URL de validation
  2. Beryl ouvre le navigateur (macOS open, Linux xdg-open)
  3. L'utilisateur clique « autoriser » dans OVH
  4. Beryl attend une confirmation (Entrée)
  5. La consumer_key est stockée dans env
Source
capabilities

Capabilities que ce provider expose (ADR-014). Valeurs connues :

:dns — gestion d'une zone DNS (records, reverse, refresh) :compute — hébergement de serveurs (rescue, boot_from_disk…) :object_storage — stockage objet compatible S3 (futur) :cdn — CDN (futur) :cert — certificats SSL (futur)

Un provider peut en avoir plusieurs (OVH = [:dns, :compute]). Beryl vérifie la capability avant d'appeler une méthode correspondante — si un dns_provider: hetzner est déclaré alors qu'Hetzner n'a pas :dns, une erreur explicite est levée à la résolution.

Par défaut vide : chaque sous-classe doit la définir.

Source
commercial_range(service_name : String) : String | Nil

Gamme commerciale OVH seule (ex. "Advance-2"). Délègue à server_detail.

Source
compute_task_done?(status : String) : Bool

Vrai si l'état status correspond à un terminal de succès ("done", "completed", etc. selon les providers).

Source
compute_task_status(task_id : String) : String

État d'une task ("init", "doing", "done", "ovhError"…). Convention : si la task est en état terminal de succès, task_done? est vrai.

Source
credentials_env_vars

Variables d'environnement nécessaires pour que available? soit vrai et que les appels API marchent. Listées dans l'ordre où beryl init les demandera si le provider n'est pas configuré. Utilisé pour la configuration interactive + aide.

Source
credentials_help_details

Détails affichés pendant beryl init : beryl génère la consumer key automatiquement via POST /auth/credential, l'opérateur n'a donc qu'à cocher « autoriser » dans le navigateur. Les routes listées ci-dessous sont injectées automatiquement par le hook bootstrap_credentials_if_needed — documentation uniquement.

Source
credentials_help_url

URL d'aide côté panel hébergeur où l'utilisateur génère les credentials (token d'API, secret key, etc.). Affichée avant le prompt interactif pour que l'opérateur ouvre sa page dans un autre onglet.

Source
dedicated_server_names

Liste les serviceName de TOUS les serveurs dédiés du compte (GET /dedicated/server). Sert à beryl scan --discover.

Source
display_name

Nom humain pour affichage (ex: "OVHcloud", "Scaleway Elastic Metal").

Source
ensure_record(zone : String, field_type : String, sub_domain : String, target : String) : Nil

Enregistre (ou met à jour) un record DNS. Idempotent : si le record existe déjà avec la même valeur, no-op.

zone → nom de la zone (ex: "example.net") field_type → type DNS ("A", "AAAA", "CNAME", "TXT"…) sub_domain → sous-partie ("loulou" ou "" pour le root) target → valeur du record (IP, FQDN, …)

Source
find_dedicated_server_by_ip(ip : String) : String | Nil

Cherche le service_name d'un serveur dédié OVH par son IP principale (v4). nil si aucun match. Itère /dedicated/server puis lit l'ip de chacun — permet de scanner un host par FQDN/IP sans connaître son service_name. Couvert par GET /dedicated/server + /dedicated/server/*.

Source
hardware_disk_inventory(service_name : String) : Beryl::DiskInventory | Nil

Inventaire disques déclaré par OVH (specifications/hardware). nil si l'API ne répond pas ou n'expose pas diskGroups. flash = SSD+NVMe, spinning = HDD ; total = somme de TOUS les disques déclarés. Couvert par le droit GET /dedicated/server/* (pas de re-régén de clé).

Source
ip_to_service_index

Index { IP principale => service_name } de TOUS les serveurs dédiés du compte, en UNE passe (liste + un GET par serveur). Sert au batch beryl info --refresh : résoudre le service_name de chaque host par son IP sans relister à chaque fois. Couvert par GET /dedicated/server*.

Source
ipv6_address(service_name : String) : String | Nil

Adresse IPv6 HÔTE : <bloc>::1 du bloc ROUTÉ vers le serveur (specifications/network → routing.ipv6.ip ; PAS .network, le /48 agrégé de la baie, commun à tous ses serveurs), à défaut le premier bloc de /ips — qui peut lister un ancien bloc (vu sur des ADV recyclés : bloc /64 d'un ancien serveur listé, /56 réellement routé).

Source
ipv6_block(service_name : String) : String | Nil

Bloc IPv6 du serveur en forme canonique (2001:41d0:364:3e00::/56) : routing.ipv6.ip, sinon le premier v6 de /ips. nil si aucun. → ovh.ipv6_block.

Source
ipv6_gateway(service_name : String) : String | Nil

Adresse IPv6 publique HÔTE du serveur (ex. "2001:41d0:250:dd00::1"), dérivée du bloc GET /dedicated/server/{s}/ips (1ʳᵉ IP contenant ':', suffixe /64 retiré). On force le suffixe hôte ::1 : le bloc nu …dd00:: est l'anycast Subnet-Router (reste en DAD tentative, inutilisable) — cf. primitive netif6. nil si aucune IPv6. Best-effort. Passerelle IPv6 que le serveur DOIT utiliser, d'après OVH (specifications/network → routing.ipv6.gateway) : fe80::1 sur certaines gammes, une adresse globale hors du /64 (<bloc /56>ff:ff:ff:ff:ff) sur d'autres (qwerty, loulou, quantas, tika). nil si l'API ne répond pas.

Source
list_ssh_keys

Liste les clés SSH enregistrées côté panel de l'hébergeur, avec leur contenu public. beryl init utilise le contenu pour matcher avec les ~/.ssh/*.pub locaux — si une clé distante correspond à un fichier local (type + base64 identiques, le commentaire peut différer), on auto-détecte le mapping sans prompt.

Lève si les credentials sont présents mais l'API refuse.

Source
list_vracks

Noms de service des vRacks du compte (ex. "pn-12345").

Source
monthly_price(service_name : String) : String | Nil

Prix de renouvellement RAMENÉ AU MOIS (HT, devise du compte), via serviceInfos → serviceId → GET /services/{id}. Le champ billing.pricing.price.value est le prix de la PÉRIODE de facturation : un serveur facturé à l'année (constaté : KS-C à 136.69/an) doit être divisé par 12. Période lue dans billing.pricing.duration, sinon billing.renew.current.period (ISO 8601 : P1M, P1Y, P12M…) ; absente → mensuel. Chaîne formatée "89.99", nil si l'API ne répond pas / ne l'expose pas / droit GET /services/* absent. Best-effort.

Source
name

Identifiant court et stable du provider (ex: "ovh", "scaleway"). Utilisé comme clé dans le registre, comme valeur de provider: dans le YAML d'un host, et comme nom dans les logs.

Source
network_raw(service_name : String) : NamedTuple(ips: JSON::Any | Nil, network: JSON::Any | Nil)

Réponses BRUTES de l'API réseau d'un serveur (lecture seule), pour un diagnostic ou un ticket : GET /dedicated/server/{s}/ips (toutes les IP et blocs) et GET …/specifications/network (entier, dont routing.ipv6). nil pour une réponse en erreur.

Source
owns?(host_name : String) : Bool

Vrai si ce provider héberge le serveur identifié par host_name. Conservé pour compat (ex: diagnostics). La résolution d'hôte n'en a plus besoin : elle passe par suffix-match et recherche dans les fichiers (voir Beryl::Config::Root#resolve). Cette méthode peut être utilisée par les sous-commandes qui veulent confirmer qu'un serveur existe bien chez le provider.

Source
refresh_zone(zone : String) : Nil

Déclenche un refresh de la zone côté provider (nécessaire chez OVH pour propager un record fraîchement ajouté).

Source
request_rescue(resource_id : String, ssh_key_ref : String) : String

Demande un reboot en rescue mode côté panel. Le provider pose la clé SSH qu'il veut que beryl utilise pour se connecter. Retourne un identifiant de task (numérique OVH, ou UUID Scaleway) qu'on peut interroger avec compute_task_status.

Source
required_access_rules

Routes OVH que beryl appelle. Injectées dans la consumer_key au moment de la création via POST /auth/credential. Les * sont des wildcards supportés par OVH (pattern « tout sous ce préfixe avec un / »). ATTENTION : /me/sshKey/* NE matche PAS /me/sshKey nu (endpoint de liste). Il faut les deux :

  • /me/sshKey → GET pour lister les noms de clés
  • /me/sshKey/* → GET pour récupérer le détail d'une clé Même logique pour /dedicated/server et /domain/zone.
Source
server_detail(service_name : String) : NamedTuple(commercial: String | Nil, datacenter: String | Nil, rack: String | Nil, ipv4: String | Nil)

Gamme commerciale OVH du serveur (ex. "Advance-2"), via GET /dedicated/server/{s} → champ commercialRange. nil si l'API ne répond pas. Couvert par le droit GET /dedicated/server/*. Détails d'un serveur dédié en UN GET (/dedicated/server/{s}) : gamme commerciale, datacentre (datacenter, ex. gra3), baie (rack), IPv4 primaire (ip). Champs nil si absents / API muette. Couvert par GET /dedicated/server/*.

Source
server_display_name(service_name : String) : String | Nil

Nom du serveur TEL QU'AFFICHÉ dans l'interface OVH (ex. "qwerty") : iam.displayName de GET /dedicated/server/{sn}. ⚠️ PAS resource.displayName de /services/{id} : celui-ci peut valoir le reverse DNS ou un FQDN (constaté le 1ᵉʳ oct. 2026 : interface « qwerty », /services « qwert.yuiop.ovh »). /services ne sert plus que de repli si le bloc iam est absent. nil si non défini, égal au serviceName, ou API muette. Best-effort.

Source
server_hardware(service_name : String) : Beryl::HardwareSpec | Nil

Caractéristiques matérielles DÉCLARÉES par OVH (specifications/hardware) : CPU, cœurs/threads, RAM, disques. nil si l'API ne répond pas. Lecture seule, droit GET /dedicated/server/*.

Source
set_reverse(ip : String, reverse : String, block : String | Nil = nil) : Nil

Pose (ou met à jour) le reverse DNS d'une IP. reverse est un FQDN (avec ou sans point final). block : bloc IP déclaré par l'hébergeur qui contient ip (utile en IPv6 où le reverse se pose au niveau du bloc) ; nil → déduit par le provider.

Source
ssh_key_yaml_fragment(key_id : String) : Hash(String, String | Array(String))

Côté YAML, OVH attend ovh.ssh_key_name: <label>. Le label est directement le keyName OVH. Consommé par beryl rescue qui passe cette valeur à client.dedicated_servers.prepare_rescue.

Source
vrack_interface(service_name : String) : Tuple(String, String | Nil) | Nil

Interface vRack d'un serveur (modèle vRack 2.0) : {uuid, vRack-actuel ou nil}. nil si le serveur n'a aucune interface en mode vrack (gamme sans support vRack). OVH a deux modèles : legacy (dedicatedServer, par nom) et 2.0 (dedicatedServerInterface, par UUID d'interface) — les serveurs récents (double NIC) sont en 2.0.

Source
vrack_of_server(service_name : String) : String | Nil

Le vRack contenant ce serveur (via son interface vRack), nil si aucun.

Source
vrack_task_status(vrack : String, task_id : String) : String | Nil

Statut d'une task vRack ("todo" / "doing" / "done" / "error"…). nil si introuvable (souvent : la task est terminée et OVH l'a purgée).

Source