This is a prometheus exporter for Junos devices using PyEZ Tables and Views.
PyEZ Tables and Views allows you to extract operational information from Junos devices and map it to python objects using simple YAML definitions.
junos-exporter
can convert the python objects created with PyEZ into prometheus metrics.
As a result, this exporter allows you to flexibly configure the metrics to be scraped using only YAML definitions.
The following configuration is required for Junos devices because PyEZ using netconf over ssh.
set system service netconf ssh
pip install junos-exporter
Docker(Recommended)
-
Download
config.yml
:curl -sO https://raw.githubusercontent.com/minefuto/junos-exporter/refs/heads/main/config.yml
-
Edit
config.yml
:general: prefix: junos # Prefix for the metrics timeout: 60 # Request timeout for the exporter credentials: default: username: admin # Junos device's username password: admin@123 # Junos device's password
-
Update prometheus configuration:
scrape_configs: - job_name: "junos-exporter" static_configs: - targets: - "192.168.1.1" # Target device relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 127.0.0.1:9326
-
Run the exporter using Docker:
docker run -v config.yml:/app/config.yml ghcr.io/minefuto/junos-exporter
Pip
-
Download
config.yml
:curl -s -o ~/.junos-exporter/config.yml --create-dirs https://raw.githubusercontent.com/minefuto/junos-exporter/refs/heads/main/config.yml
-
Edit
config.yml
:general: prefix: junos # Prefix for the metrics timeout: 60 # Request timeout for the exporter credentials: default: username: admin # Junos device's username password: admin@123 # Junos device's password
-
Download
op/tables.yml
:curl -s -o ~/.junos-exporter/op/tables.yml --create-dirs https://raw.githubusercontent.com/minefuto/junos-exporter/refs/heads/main/op/tables.yml
-
Update prometheus configuration:
scrape_configs: - job_name: "junos-exporter" static_configs: - targets: - "192.168.1.1" # Target device relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 127.0.0.1:9326
-
Run the exporter:
junos-exporter
junos-exporter
has several uvicorn
options:
usage: junos-exporter [-h] [--host HOST] [--log-level {critical,error,warning,info,debug,trace}]
[--no-access-log] [--port PORT] [--reload] [--root-path ROOT_PATH] [--workers WORKERS]
options:
-h, --help Show this help message and exit
--host HOST Listen address [default: 0.0.0.0]
--log-level Log level [default: info]
--no-access-log Disable access log
--port PORT Listen port [default: 9326]
--reload Enable auto reload
--root-path ROOT_PATH
Root path [default: ""]
--workers WORKERS Number of worker processes [default: 1]
This exporter allows you configure the authentication method per Junos device.
Add the module_name defined in the credentials
section of config.yml
to the query parameter when scraping.
e.g. https://localhost:9326/metrics?credential=vjunos&target=192.168.10.12
By setting __params_credential
to vjunos
in the prometheus configuration, vjunos
credential will be used.
If credential
is not specified as a query parameter, predefined credential(default
) is used when scraping.
credentials:
vjunos: # password authentication
username: admin
password: admin@123
vjunos_key: # public key authentication
username: admin
private_key: ~/.ssh/id_rsa
private_key_passphrase: admin@123 # option
scrape_configs:
- job_name: "junos-exporter"
static_configs:
- targets:
- "192.168.1.1" # Target device using default credential
- targets:
- "192.168.1.2" # Target device using "vjunos" credential
labels:
__params_credential: "vjunos"
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: 127.0.0.1:9326
This exporter allows you configure the metrics to be scraped per Junos device.
Add the module_name defined in the modules
section of config.yml
to the query parameter when scraping.
e.g. https://localhost:9326/metrics?module=router&target=192.168.10.12
By setting __params_module
to router
in the prometheus configuration, router
module will be used.
If module
is not specified as a query parameter, predefined module(default
) is used when scraping.
A module named default
is predefined, providing metrics such as:
- alarm information from
show system alarm/show chassis alarm
- fpc status and cpu/memory utilization from
show chassis fpc
- module information/status from
show chassis hardware/show chassis environment
- routing engine status and cpu/memory utilization from
show chassis routing-engine
- storage utilization from
show system storage
- interface status/error/drop/statistics from
show interface extensive
- interface tx/rx power from
show interface diatnostics optics
- lldp status from
show lldp neighbor
- lacp status from
show lacp interface
- route count from
show route summary
- arp information from
show arp expiration-time
- ospf status/cost from
show ospf neighbor extensive/show ospf interface
- bgp status/prefix count from
show bgp summary
- vrrp status from
show vrrp
- bfd status from
show bfd session
You can extract python objects from any command on Junos device and convert it into prometheus metrics by defining YAML configurations.
Here is an example to create custom metrics using the show interface extensive
command output.
-
Define PyEZ Tables and Views
Place the YAML file for PyEZ Tables and Views in the following directories:
- PyEZ structured & unstructured tables and views configuration files:
- Docker:
/app/op/
- Pip:
~/.junos-exporter/op/
- Docker:
- TextFSM template files:
- Docker:
/app/textfsm/
- Pip:
~/.junos-exporter/textfsm/
- Docker:
Below is the mapping information from the
show interface extensive
command to python objects:PhysicalInterfaceStatus: rpc: get-interface-information args: extensive: True interface_name: '[afgxe][et]-*' key: name item: physical-interface view: PhysicalInterfaceStatusView PhysicalInterfaceStatusView: groups: traffic_statistics: traffic-statistics input_error_list: input-error-list output_error_list: output-error-list ethernet_pcs_statistics: ethernet-pcs-statistics fields: oper_status: oper-status admin_status: admin-status description: description speed: speed mtu: mtu link_mode: link-mode interface_flapped: interface-flapped fields_traffic_statistics: input_bytes: input-bytes input_packets: input-packets output_bytes: output-bytes output_packets: output-packets fields_input_error_list: input_errors: input-errors input_drops: input-drops framing_errors: framing-errors input_runts: input-runts input_discards: input-discards input_l3_incompletes: input-l3-incompletes input_l2_channel_errors: input-l2-channel-errors input_l2_mismatch_timeouts: input-l2-mismatch-timeouts input_fifo_errors: input-fifo-errors input_resource_errors: input-resource-errors fields_output_error_list: carrier_transitions: carrier-transitions output_errors: output-errors output_drops: output-drops collisions: output-collisions aged_packets: aged-packets mtu_errors: mtu-errors hs_link_crc_errors: hs-link-crc-errors output_fifo_errors: output-fifo-errors output_resource_errors: output-resource-errors fields_ethernet_pcs_statistics: bit_error_seconds: bit-error-seconds errored_blocks_seconds: errored-blocks-seconds
Refer to Juniper's documentation for more details:
Currently unsupported features:
- Parsing unstructured output for
Target FPC
- Nested table definitions, such as PyEZ LacpPortTable
- PyEZ structured & unstructured tables and views configuration files:
-
Map python objects to metrics
Edit the
optables
section inconfig.yml
:optables: PhysicalInterfaceStatus: # PyEZ table name metrics: - name: interface_speed # Metric name value: speed # Metric value type: gauge # Metric type (gauge, count, or untyped) help: Speed of show interfaces extensive # Metric description value_transform: # (Optional) Transform string values into floats 100mbps: 100000000 1000mbps: 1000000000 1Gbps: 1000000000 10Gbps: 10000000000 100Gbps: 100000000000 _: 0 # (Optional) Fallback value for unknown strings (default: NaN) - name: interface_lastflap_seconds value: interface_flapped type: counter help: Last flapped of show interfaces extensive to_unixtime: True # Convert timestamps to Unix time labels: - name: interface # (Optional) Label name value: name # Label value - name: description value: description
With the above configuration, the following metrics can be scraped:
# HELP junos_interface_speed Speed of show interfaces extensive # TYPE junos_interface_speed gauge junos_interface_speed{interface="ge-0/0/0",description="description example"} 1000000000.0 # HELP junos_interface_lastflap_seconds_total Last flapped of show interfaces extensive # TYPE junos_interface_lastflap_seconds_total counter junos_interface_lastflap_seconds_total{interface="ge-0/0/0",description="description example"} 1745734677000.0
Additional Notes:
-
PyEZ Table key are automatically mapped to
key
andname
and can be used inmetrics value
orlabel value
.- op/tables.yml
RoutingEngineStatus: rpc: get-route-engine-information item: route-engine key: slot <- !! view: RoutingEngineStatusView
- config.yml
RoutingEngineStatus: metrics: -snip- labels: - name: slot value: key
- op/tables.yml
-
If there are multiple keys, they will be assigned as
key.0
,key.1
, etc.- op/tables.yml
LldpStatus: rpc: get-lldp-neighbors-information item: lldp-neighbor-information key: - lldp-local-port-id # key.0, name.0 - lldp-remote-port-id # key.1, name.1 view: LldpStatusView
- config.yml
LldpStatus: metrics: - name: lldp_neighbor_info value: 1 type: gauge help: Information of show lldp neighbor labels: - name: remote value: remote_system_name - name: interface value: key.0 - name: remote_interface value: key.1
-
You can assign fixed values for the metric:
- config.yml
HardwareStatus: metrics: - name: hardware_info value: 1 <- !! type: gauge help: Information of show chassis hardware
-
-
Create a Module
Add the table name to the
modules
section inconfig.yml
:modules: router: tables: - PhysicalInterfaceStatus
Update prometheus configuration to use the new module:
scrape_configs: - job_name: "junos-exporter" static_configs: - targets: - "192.168.1.1" # Target device - "192.168.1.2" labels: __params_module: "router" relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 127.0.0.1:9326
MIT