feat(yang): add validated YANG paths for Arista EOS 4.35.0F
This commit is contained in:
319
src/gnmi_eos/yang/paths.py
Normal file
319
src/gnmi_eos/yang/paths.py
Normal 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]
|
||||
Reference in New Issue
Block a user