feat(yang): add validated YANG paths for Arista EOS 4.35.0F

This commit is contained in:
2026-04-01 09:53:29 +00:00
parent e1a14b0fbb
commit f0ed540eff

319
src/gnmi_eos/yang/paths.py Normal file
View File

@@ -0,0 +1,319 @@
"""
YANG Path constants for Arista EOS 4.35.0F.
Provides validated gNMI YANG paths for Arista EOS devices.
Usage:
from gnmi_eos.yang.paths import Interfaces, BGP, VXLAN, FabricSubscriptions
path = Interfaces.counters("Ethernet1")
path = BGP.NEIGHBORS
path = VXLAN.VLAN_TO_VNIS
"""
from dataclasses import dataclass
# ==============================================================================
# Interfaces (OpenConfig)
# ==============================================================================
class Interfaces:
"""OpenConfig interface paths."""
ROOT = "/interfaces/interface"
@staticmethod
def interface(name: str) -> str:
"""Get path for specific interface."""
return f"/interfaces/interface[name={name}]"
@staticmethod
def config(name: str) -> str:
"""Get interface config path."""
return f"/interfaces/interface[name={name}]/config"
@staticmethod
def state(name: str) -> str:
"""Get interface state path."""
return f"/interfaces/interface[name={name}]/state"
@staticmethod
def oper_status(name: str) -> str:
"""Get interface operational status path."""
return f"/interfaces/interface[name={name}]/state/oper-status"
@staticmethod
def admin_status(name: str) -> str:
"""Get interface admin status path."""
return f"/interfaces/interface[name={name}]/state/admin-status"
@staticmethod
def counters(name: str) -> str:
"""Get interface counters path."""
return f"/interfaces/interface[name={name}]/state/counters"
# ==============================================================================
# Loopbacks (OpenConfig)
# ==============================================================================
class Loopbacks:
"""Loopback interface paths."""
LOOPBACK0 = "/interfaces/interface[name=Loopback0]"
LOOPBACK1 = "/interfaces/interface[name=Loopback1]" # VTEP source
@staticmethod
def interface(index: int) -> str:
"""Get loopback interface path."""
return f"/interfaces/interface[name=Loopback{index}]"
@staticmethod
def config(index: int) -> str:
"""Get loopback config path."""
return f"/interfaces/interface[name=Loopback{index}]/config"
# ==============================================================================
# VLANs (OpenConfig)
# ==============================================================================
class VLANs:
"""OpenConfig VLAN paths."""
ROOT = "/network-instances/network-instance[name=default]/vlans/vlan"
@staticmethod
def vlan(vlan_id: int) -> str:
"""Get specific VLAN path."""
return f"/network-instances/network-instance[name=default]/vlans/vlan[vlan-id={vlan_id}]"
@staticmethod
def config(vlan_id: int) -> str:
"""Get VLAN config path."""
return f"/network-instances/network-instance[name=default]/vlans/vlan[vlan-id={vlan_id}]/config"
@staticmethod
def svi(vlan_id: int) -> str:
"""Get SVI interface path."""
return f"/interfaces/interface[name=Vlan{vlan_id}]"
# ==============================================================================
# BGP (OpenConfig)
# ==============================================================================
class BGP:
"""OpenConfig BGP paths."""
_BASE = "/network-instances/network-instance[name=default]/protocols/protocol[identifier=BGP][name=BGP]/bgp"
GLOBAL = f"{_BASE}/global"
GLOBAL_CONFIG = f"{_BASE}/global/config"
GLOBAL_STATE = f"{_BASE}/global/state"
NEIGHBORS = f"{_BASE}/neighbors/neighbor"
@staticmethod
def neighbor(address: str) -> str:
"""Get specific neighbor path."""
return f"{BGP._BASE}/neighbors/neighbor[neighbor-address={address}]"
@staticmethod
def neighbor_config(address: str) -> str:
"""Get neighbor config path."""
return f"{BGP._BASE}/neighbors/neighbor[neighbor-address={address}]/config"
@staticmethod
def neighbor_state(address: str) -> str:
"""Get neighbor state path."""
return f"{BGP._BASE}/neighbors/neighbor[neighbor-address={address}]/state"
@staticmethod
def neighbor_session_state(address: str) -> str:
"""Get neighbor session state path."""
return f"{BGP._BASE}/neighbors/neighbor[neighbor-address={address}]/state/session-state"
@staticmethod
def neighbor_afi_safi(address: str, afi_safi: str) -> str:
"""Get neighbor AFI-SAFI path.
Args:
address: Neighbor IP address
afi_safi: AFI-SAFI name (e.g., 'IPV4_UNICAST', 'L2VPN_EVPN')
"""
return f"{BGP._BASE}/neighbors/neighbor[neighbor-address={address}]/afi-safis/afi-safi[afi-safi-name={afi_safi}]"
class AfiSafi:
"""BGP AFI-SAFI identifiers."""
IPV4_UNICAST = "IPV4_UNICAST"
IPV6_UNICAST = "IPV6_UNICAST"
L2VPN_EVPN = "L2VPN_EVPN"
# ==============================================================================
# VXLAN (Arista Experimental)
# ==============================================================================
class VXLAN:
"""
Arista VXLAN paths.
Model: arista-exp-eos-vxlan (augments openconfig-interfaces)
"""
INTERFACE = "/interfaces/interface[name=Vxlan1]"
ROOT = "/interfaces/interface[name=Vxlan1]/arista-vxlan"
CONFIG = "/interfaces/interface[name=Vxlan1]/arista-vxlan/config"
STATE = "/interfaces/interface[name=Vxlan1]/arista-vxlan/state"
SOURCE_INTERFACE = "/interfaces/interface[name=Vxlan1]/arista-vxlan/config/src-ip-intf"
UDP_PORT = "/interfaces/interface[name=Vxlan1]/arista-vxlan/config/udp-port"
VLAN_TO_VNIS = "/interfaces/interface[name=Vxlan1]/arista-vxlan/vlan-to-vnis"
VRF_TO_VNIS = "/interfaces/interface[name=Vxlan1]/arista-vxlan/vrf-to-vnis"
@staticmethod
def vlan_to_vni(vlan_id: int) -> str:
"""Get specific VLAN-to-VNI mapping path."""
return f"/interfaces/interface[name=Vxlan1]/arista-vxlan/vlan-to-vnis/vlan-to-vni[vlan={vlan_id}]"
@staticmethod
def vrf_to_vni(vrf_name: str) -> str:
"""Get specific VRF-to-VNI mapping path."""
return f"/interfaces/interface[name=Vxlan1]/arista-vxlan/vrf-to-vnis/vrf-to-vni[vrf={vrf_name}]"
# ==============================================================================
# MLAG (Arista Experimental)
# ==============================================================================
class MLAG:
"""
Arista MLAG paths.
Model: arista-exp-eos-mlag
WARNING: Only config is exposed via gNMI.
State (peer status, role) requires eAPI.
"""
ROOT = "/arista/eos/mlag"
CONFIG = "/arista/eos/mlag/config"
# ==============================================================================
# EVPN (Arista Experimental)
# ==============================================================================
class EVPN:
"""
Arista EVPN paths.
Model: arista-exp-eos-evpn
WARNING: Only config is exposed via gNMI.
Learned routes/MACs require eAPI.
"""
ROOT = "/arista/eos/evpn"
INSTANCES = "/arista/eos/evpn/evpn-instances"
@staticmethod
def instance(name: str) -> str:
"""Get specific EVPN instance path."""
return f"/arista/eos/evpn/evpn-instances/evpn-instance[name={name}]"
@staticmethod
def instance_config(name: str) -> str:
"""Get EVPN instance config path."""
return f"/arista/eos/evpn/evpn-instances/evpn-instance[name={name}]/config"
@staticmethod
def route_target(name: str) -> str:
"""Get EVPN instance route-target path."""
return f"/arista/eos/evpn/evpn-instances/evpn-instance[name={name}]/route-target/config"
# ==============================================================================
# Port-Channel / LAG (OpenConfig)
# ==============================================================================
class PortChannel:
"""OpenConfig LAG/Port-Channel paths."""
@staticmethod
def interface(channel_id: int) -> str:
"""Get port-channel interface path."""
return f"/interfaces/interface[name=Port-Channel{channel_id}]"
@staticmethod
def config(channel_id: int) -> str:
"""Get port-channel config path."""
return f"/interfaces/interface[name=Port-Channel{channel_id}]/config"
@staticmethod
def aggregation(channel_id: int) -> str:
"""Get LAG aggregation config path."""
return f"/interfaces/interface[name=Port-Channel{channel_id}]/aggregation/config"
@staticmethod
def aggregation_state(channel_id: int) -> str:
"""Get LAG aggregation state path."""
return f"/interfaces/interface[name=Port-Channel{channel_id}]/aggregation/state"
# ==============================================================================
# System (OpenConfig)
# ==============================================================================
class System:
"""OpenConfig system paths."""
ROOT = "/system"
CONFIG = "/system/config"
STATE = "/system/state"
HOSTNAME = "/system/config/hostname"
# ==============================================================================
# Subscription Helpers
# ==============================================================================
@dataclass
class SubscriptionPath:
"""Helper for building subscription paths."""
path: str
mode: str = "on-change" # on-change, sample, target-defined
def __str__(self) -> str:
return self.path
class FabricSubscriptions:
"""Pre-defined subscription paths for fabric monitoring."""
INTERFACE_STATE = SubscriptionPath("/interfaces/interface/state")
BGP_SESSIONS = SubscriptionPath(
"/network-instances/network-instance[name=default]/protocols/protocol[identifier=BGP][name=BGP]/bgp/neighbors/neighbor/state/session-state"
)
VXLAN_VNIS = SubscriptionPath(
"/interfaces/interface[name=Vxlan1]/arista-vxlan/vlan-to-vnis"
)
@classmethod
def all(cls) -> list[SubscriptionPath]:
"""Get all fabric subscription paths."""
return [cls.INTERFACE_STATE, cls.BGP_SESSIONS, cls.VXLAN_VNIS]