Endpoints HTTP
El agente es un servidor HTTP y nada más: no abre ninguna conexión saliente.
Cada ruta, parámetro, código de estado y formato de línea de abajo está leído
de internal/agent/http.go, internal/, internal/agent/source.go
y internal/.
Dirección de escucha
Sección titulada «Dirección de escucha»El agente escucha en ADDR:PORT. install escribe la dirección de la propia
veth del agente en ADDR y 9123 en PORT, así que por defecto es
http://172.30.10.2:9123. Un agente arrancado sin ADDR escucha en :PORT,
todas las direcciones dentro del espacio de nombres de red de su contenedor.
Cómo llega un equipo a esa dirección, directamente, por el relay o mediante
--expose, está en Acceso por red.
El servidor le da a un cliente 5 s para enviar las cabeceras de su petición y
30 s para recibir una respuesta (ReadHeaderTimeout y WriteTimeout en el
fichero internal/agent/agent.go).
Autenticación
Sección titulada «Autenticación»Sin TOKEN, todas las rutas están abiertas. Con él, todas las rutas salvo
/healthz necesitan:
Authorization: Bearer <token>El valor de la cabecera se compara con el token después de quitarle un
prefijo "Bearer " opcional, así que también se acepta una cabecera que lleve
el token a secas. Una petición sin ella, o con otro token, recibe 401, el
cuerpo
token required y WWW-Authenticate: Bearer. /healthz sigue abierta a
propósito: lleva la cadena de la placa que se le pide enviar a un operador
cuando su placa no tiene mapa de puertos, y para eso no debería hacer falta un
token.
El transporte por relay no puede presentar un token. /tool fetch se ejecuta en
el router y no envía cabecera Authorization
(internal/), así que en un agente con token el relay
llega a /healthz y a nada más. --expose hace obligatorio el token; Exponer
en la LAN cuenta por qué.
Endpoints
Sección titulada «Endpoints»| Método | Ruta | Token | Devuelve |
|---|---|---|---|
GET |
/healthz |
no | JSON: si está vivo, números de secuencia, relojes, cadencia, ticks retrasados, hash de capacidades, placa |
GET |
/capabilities |
sí | JSON: el kernel, la placa, las fuentes, los techos propios del equipo y la cadencia de cada fuente |
GET |
/snapshot |
sí | NDJSON, y cierra: los últimos N segundos, o hasta N muestras tras un número de secuencia |
GET |
/stream |
sí | NDJSON, por trozos y abierto: relleno desde un número de secuencia, y luego en directo |
GET |
/sampler |
sí | JSON: lo que solo el agente puede contar de sí mismo — ticks, slips, disparos, capturas |
GET |
/captures |
sí | JSON: el índice de capturas |
GET |
/captures/{id} |
sí | NDJSON: una línea de cabecera de la captura y luego las líneas de muestra tal cual |
DELETE |
/captures/{id} |
sí | 204, y los bytes de la captura vuelven al presupuesto |
POST |
/capture |
sí | JSON: arma una captura ahora |
Desliza en horizontal para ver todas las columnas
Cualquier otra ruta es 404; una ruta conocida con otro método la rechaza el
enrutador de Go con 405.
GET /healthz
Sección titulada «GET /healthz»| Campo | Tipo | Significado |
|---|---|---|
ok |
bool | siempre true cuando el agente responde |
seq |
entero | número de secuencia de la muestra más reciente del anillo; 0 cuando el anillo está vacío |
oldest_seq |
entero | número de secuencia más antiguo que aún se guarda; 0 cuando está vacío |
wall_ns |
entero | el reloj de pared del router en la respuesta, ns desde la época |
mono_ns |
entero | el reloj monotónico del agente en la respuesta, ns |
uptime_s |
float | segundos desde que se montó el servidor HTTP |
rate_hz |
entero | cadencia configurada del muestreador |
slipped |
entero | ticks cuya lectura terminó después de que tocara el siguiente tick |
capabilities_hash |
cadena | ocho dígitos hexadecimales sobre el kernel, el número de núcleos y las fuentes activas |
version |
cadena | la identidad de compilación del agente |
board |
cadena | la cadena del modelo del device tree; se omite cuando la placa no tiene |
boot_id |
cadena | el identificador de arranque del kernel, leído al arrancar; se omite cuando el kernel no lo publica |
Desliza en horizontal para ver todas las columnas
Lo que lee de ella la CLI:
wall_ns, frente a su propio reloj, para medir el desfase;seqyoldest_seq, para planificar un relleno;capabilities_hash, para darse cuenta de que ha cambiado el kernel o el conjunto de fuentes que tiene debajo;- en
doctor,boardpara clasificar los registros del log del kernel, yseqconoldest_seqpara leer el anillo entero, o sus 10 000 muestras más recientes cuando guarda más; - en
forward,boot_id, para distinguir un router que se reinició de un agente cuyo contenedor se reinició solo: el contenedor comparte el kernel del router, así que solo un reinicio le da un identificador nuevo (reboot).
Ningún código de la CLI lee mono_ns. El sondeo que sigue a install imprime
esta respuesta así:
direct transport ok: agent <version>, <rate> Hz, seq <n>, <n> slipped, <rtt> round trip<version> es la versión de la propia CLI, que esta graba en el agente que
compila (dev cuando la CLI se compiló sin ella).
GET /capabilities
Sección titulada «GET /capabilities»Lo que el agente estableció al arrancar sobre el kernel y la placa, sin la API de RouterOS. Datos del equipo es este objeto tal como el colector se lo entrega a cada destino.
| Campo | Significado |
|---|---|
kernel |
la cadena de versión del kernel de /proc/version |
board |
el modelo del device tree; se omite cuando no existe |
ports |
nombre de interfaz del kernel a nombre por defecto de RouterOS, para una placa de la tabla de puertos; si no, se omite |
ports_from |
cómo se estableció ese mapa; se omite con él |
cores |
número de núcleos, de la primera lectura de /proc/stat |
user_hz |
USER_HZ, la unidad de los contadores de ticks |
sources |
nombre de fuente a true cuando el fichero se abrió y la fuente está activa: stat, meminfo, loadavg, softnet, softirqs, interrupts, vmstat, psi, schedstat, self, yaffs, diskstats, slabinfo, kmsg, thermal, cpufreq, perf, buddyinfo, mtd |
namespaced |
lo que el agente a propósito no lee como datos del router: net/dev, net/snmp, net/netstat, sys/ |
cgroup |
true cuando se puede leer el cpu.stat de cgroup2, así que el coste propio es exacto |
privileged |
true cuando se abrieron tanto /proc/slabinfo como /dev/kmsg |
limits |
los techos propios del equipo, leídos una vez al arrancar (abajo) |
cadences |
nombre de fuente a {"hz", "reason"} para cada fuente de nivel (abajo) |
hash |
el mismo valor que el capabilities_hash de /healthz |
Desliza en horizontal para ver todas las columnas
limits no tiene etiquetas JSON en el código, así que sus claves son los
nombres de campo de Go: ThermalCriticalMilliC (zona al punto de disparo
crítico más bajo, m°C), ThermalPollingMS (zona a su polling_delay),
CPUFreqMinKHz, CPUFreqMaxKHz, CPUFreqStepsKHz (núcleo a su escalera),
CPUFreqGovernor, CPUFreqRelated (núcleo a los núcleos que cambian de
frecuencia con él), CgroupMemoryMaxBytes y ConntrackMax. Un techo que el
equipo no publica queda vacío o a 0.
El reason de una cadencia es uno de estos: rate (se lee a la cadencia del
muestreador), declared (el equipo publica su propia cadencia de refresco),
policy (un gobernador de cpufreq userspace significa que el reloj no puede
moverse sin una escritura), budget (un coste de análisis medido), change (se
lee en cada tick y se guarda al cambiar) u override (FLOOR_HZ). Los
contadores nunca aparecen aquí, porque a los contadores nunca se les pone
suelo. Cadencias de las fuentes
explica cada suelo.
GET /snapshot
Sección titulada «GET /snapshot»Escribe NDJSON y cierra. Dos formas, según esté presente since o no.
| Parámetro | Por defecto | Acepta | Significado |
|---|---|---|---|
seconds |
1 |
1–3600 | sin since: las seconds × rate muestras más recientes que guarda el anillo, de la más antigua a la más nueva |
since |
ausente | un número de secuencia | hasta max muestras con número de secuencia mayor, de la más antigua a la más nueva; since=0 empieza por la más antigua guardada |
max |
20 |
1–10000 | con since: el máximo de muestras que lleva una respuesta |
Desliza en horizontal para ver todas las columnas
Un valor fuera de su rango es 400 con un motivo de una línea. La forma
since es de la que tiran record y forward, por los dos transportes, y con
la que doctor lee el anillo (since uno por debajo de oldest_seq, o
seq − 10 000 cuando el anillo guarda más de 10 000 muestras, max=10000, solo por el transporte directo y con el token cuando hay
TOKEN). Añade
dos tipos de línea que la forma seconds no tiene: primero una línea de hueco
cuando since es más antiguo que el anillo, y una línea de disparo antes de
cada muestra en la que saltó una condición de captura. max existe por el
relay, porque /tool fetch output=user devuelve como mucho 64 512 B y
trunca el resto sin avisar; el relay pide como
mucho 13
líneas.
GET /stream
Sección titulada «GET /stream»NDJSON por trozos con Cache-Control: no-store, abierto hasta que el cliente
se va o hasta que lo cierra el WriteTimeout de 30 s del servidor, lo que
ocurra antes.
sinceausente o0: empieza en directo. La primera muestra que se escribe es la siguiente que se produce después de la petición; la más reciente que ya estaba en el anillo no se escribe.since=N: primero rellena con todas las muestras guardadas posteriores a N y después sigue. Unsincemás antiguo que el anillo escribe primero una línea de hueco.- El anillo se consulta cada medio período y cada muestra nueva se escribe en cuanto llega, con las líneas de disparo antes de las muestras en las que saltaron.
- Cada 5 s se escribe una línea de comentario
# heartbeat seq=<newest>. Un consumidor se salta las líneas que empiezan por#.
Un since que no es un número es 400.
El WriteTimeout de 30 s del servidor se aplica a todas las respuestas, y el
servidor HTTP de Go no lo levanta para un manejador que sigue escribiendo, así
que una conexión a /stream termina unos 30 s después de abrirse por mucho que
le quede por decir: una conexión dura 30,01–30,06 s y lleva 304–306
líneas a 10 Hz. Quien consuma /stream tiene que reconectar, con el último
seq que vio como since para que se rellene el hueco. La CLI no depende de /stream: record y forward tiran de
/snapshot?since=.
Tipos de línea
Sección titulada «Tipos de línea»Aparte del comentario de latido de /stream, cada línea que escriben
/snapshot, /stream y /captures/{id} es un objeto JSON y un salto de
línea. Existen cuatro tipos, que se distinguen por su primera clave:
| Línea | Dónde | Significado |
|---|---|---|
una muestra, que empieza {"seq": |
las tres | un tick, abajo |
{"gap":{"from":F, |
/snapshot?since=, /stream |
las muestras de F a T ya no están en el anillo |
{"trigger":{…}} |
/snapshot?since=, /stream |
saltó una condición de captura en la muestra que sigue |
{"capture":{…}} |
primera línea de /captures/{id} |
la cabecera de la captura |
Desliza en horizontal para ver todas las columnas
Un consumidor que no conoce un tipo se lo salta, así que un tipo de línea que no ha visto nunca no le cuesta nada.
Línea de muestra
Sección titulada «Línea de muestra»Cada campo numérico es un delta desde la muestra anterior salvo que la
tabla diga que es un nivel. Una fuente que el kernel no tiene, o que el
despliegue no puede leer, se omite, nunca se escribe como cero. Los ticks
ocupados de un núcleo son u + n + s + q + sq + st; idle e iowait no cuentan
como ocupados.
| Campo | Tipo | Contenido |
|---|---|---|
seq |
— | número de secuencia, desde 1 |
mono_ns |
— | el reloj monotónico del agente en la lectura |
wall_ns |
— | el reloj de pared del router en la lectura, ns desde la época |
dt_ns |
— | el intervalo real desde la muestra anterior; todos los deltas son sobre este, no sobre el período nominal |
cpu |
delta | un objeto por núcleo: u user, n nice, s system, i idle, w iowait, q irq, sq softirq, st steal, en ticks de USER_HZ |
cpu_total |
delta | las mismas claves, de la propia línea resumen cpu de /proc/stat |
ctxt, intr |
delta | cambios de contexto e interrupciones de /proc/stat |
forks |
delta | la línea processes de /proc/stat |
procs_blocked |
nivel | tareas en espera no interrumpible |
psi |
delta | cpu_some, mem_some, mem_full, io_some, io_full, µs; solo en un kernel con PSI |
sched |
delta | por CPU run_ns, wait_ns; solo en un kernel con /proc/schedstat |
softnet |
delta | por CPU p procesados, d descartados, ts time squeeze |
softirq |
delta | tipo de softirq a un array de recuentos por CPU |
irq |
delta | las IRQ_TOP_K líneas de interrupción más ocupadas en este tick (8 por defecto): id, name, array cpu |
irq_total |
delta | todas las líneas de interrupción sumadas, el denominador del top-K |
irq_err |
delta | la fila Err de /proc/interrupts; se omite cuando es cero |
mem |
nivel | /proc/meminfo en kB, con claves por nombre de campo de Go: MemTotal, MemFree, MemAvailable, Buffers, Cached, Dirty, Shmem, Slab, SReclaimable, CommittedAS, Writeback, SUnreclaim, AnonPages, Mapped, KernelStack, PageTables, CommitLimit, Active, Inactive |
load |
nivel | /proc/loadavg: Load1, Load5, Load15, Running, Total, LastPID |
vm |
delta | eventos de /proc/vmstat: pgfault, pgmajfault, y cuando no son cero pgscan_kswapd, pgscan_direct, pgsteal_kswapd, pgsteal_direct, pgalloc, pgfree, allocstall, compact_stall, oom_kill, pswpin, pswpout |
vmg |
nivel | nr_free_pages, nr_dirty, nr_writeback, nr_slab_reclaimable, nr_slab_unreclaimable, en páginas |
self |
mixto | cpu_us (delta, µs), rss (nivel, bytes), cg_mem (nivel, se omite cuando es cero), cg (true cuando se leyó cgroup2), y throttled, throttled_us, oom_kill (deltas); los cuatro últimos se omiten cuando son cero o falso; wake_ns (cuánto tarde despertó el bucle tras su ticker, ns) y read_ns (cuánto tardó la lectura de todas las fuentes debidas, ns), cada uno omitido cuando es cero |
thermal |
nivel | por zona type, mc (m°C) y Celsius; a la cadencia declarada de la zona, en cada tick cuando ninguna zona declara una, o a FLOOR_HZ |
freq_khz |
nivel | por núcleo, kHz; al cambiar o en el latido de 60 s |
thermal_critical |
nivel | zona a punto de disparo crítico en m°C, en las filas que llevan thermal |
freq_max_khz |
nivel | núcleo a techo de cpufreq en kHz, en las filas que llevan freq_khz |
cgroup_mem_max |
nivel | el memory.max del contenedor en bytes, en el primer tick y una vez por latido |
flash |
mixto | por dispositivo YAFFS dev, pw, pr, er, gcc, gc (deltas) y bad, free (niveles); se omite un dispositivo sin operaciones y con los chunks libres sin cambios |
disk |
mixto | por dispositivo de bloques name, r, rs, w, ws, io_ms (deltas) e inflight (nivel); se omite un dispositivo inactivo |
slab |
nivel | caché a objetos activos; requiere privileged=yes; al cambiar, con un suelo de presupuesto |
slab_limit |
nivel | caché a su techo publicado, hoy solo nf_conntrack |
perf |
delta | por contador hardware name, array cpu, y enabled_ns, running_ns; requiere privileged=yes, y solo los contadores que implementa la CPU |
buddy |
nivel | por zona node, zone, array free indexado por orden; al cambiar o en el latido |
mtd |
nivel | por partición dev, name, corr, fail, bad, bbt, bitflip_threshold, ecc_strength; requiere privileged=yes; recuentos del kernel desde el arranque |
events |
— | registros del log del kernel en este tick: prio, lvl (0 emerg … 7 debug), fac, seq, us (µs desde el arranque), msg, y, cuando el texto nombra un puerto, iface, ros_iface y kind (link-up, link-down, stp-<estado>, own-address u other) |
events_dropped |
— | episodios de pérdida del log del kernel en este tick, no registros: uno si el tick llegó al tope de 64 registros, uno por cada desbordamiento del búfer circular del kernel (que puede suponer muchos registros); mientras no sea cero, events es una cota inferior; se omite cuando es cero |
resets |
— | contadores monotónicos que retrocedieron sin un desbordamiento de 32 bits en este tick; se omite cuando es cero |
Desliza en horizontal para ver todas las columnas
Los nombres de contadores perf que el agente intenta abrir son cycles,
instructions, cache-references, cache-misses, branch-instructions,
branch-misses y bus-cycles. Las cachés slab que guarda son nf_conntrack,
skbuff_head_cache, skbuff_fclone_cache, TCP, UDP, TCPv6, UDPv6,
sock_inode_cache, dst_cache, ip_dst_cache, kmalloc-1k y kmalloc-2k,
allí donde el kernel las tiene.
GET /sampler
Sección titulada «GET /sampler»Lo que solo el agente puede contar de sí mismo, en JSON: ticks tomados, ticks perdidos, y lo que el evaluador de disparos ha disparado, suprimido y rechazado, con lo que retiene ahora mismo y el presupuesto que esas capturas inmovilizan. Cada condición configurada está presente desde la primera lectura, a 0 hasta que se dispara — un contador que aparece con su primer suceso se lee como un hueco en la serie, no como un router tranquilo.
El colector lo lee al arrancar y luego en la misma cadencia de un minuto en la que vuelve a medir el desfase de reloj, y se lo entrega a todos los destinos. Estas cifras no son por tick, así que no pueden viajar en una muestra.
El agente no sirve /metrics. La exposición de Prometheus es la del
colector, construida desde las muestras y desde este endpoint, y lleva todas
las familias que piden los dashboards: Métricas de
Prometheus. El agente es un muestreador y
un anillo; no pliega ninguna muestra en contadores ni histogramas, y esa es
memoria que no gasta.
Capturas
Sección titulada «Capturas»Las cuatro rutas de captura responden 404 con
captures disabled (CAPTURE_MB=0) cuando el presupuesto es 0.
GET /captures
Sección titulada «GET /captures»| Campo | Significado |
|---|---|
policy |
first o last |
budget_bytes |
el presupuesto de bytes retenidos, CAPTURE_MB en bytes |
bytes |
bytes que retienen las capturas guardadas |
pending |
la captura que aún está recogiendo su ventana posterior al disparo; se omite si no hay |
captures |
las cabeceras de las capturas guardadas, de la más antigua a la más nueva |
triggers |
las condiciones configuradas, cada una con name y threshold |
Desliza en horizontal para ver todas las columnas
GET /captures/{id}
Sección titulada «GET /captures/{id}»Una línea de cabecera {"capture":{…}} y luego las líneas de muestra
exactamente como las habría escrito /snapshot, así que no hace falta ningún
analizador nuevo. Los bytes servidos se cuentan en
mikroscope_: una descarga se ejecuta en el mismo
núcleo que el muestreador. Un id que no es un número es 400; uno
desconocido es 404 con no such capture.
| Campo | Significado |
|---|---|
id |
número de captura, desde 1 |
cause |
el nombre de la condición tal como está configurada, o manual |
condition |
la condición tal como está configurada, por ejemplo busy>=0.95; vacía en una captura manual |
field |
lo que se comparó, por ejemplo cpu[2].busy_ratio; en una captura manual, el reason |
value |
el valor que la hizo saltar |
threshold |
el umbral configurado |
fire_seq |
la muestra en la que saltó |
fire_mono_ns, fire_wall_ns |
los dos relojes en el disparo |
first_seq, last_seq |
la ventana guardada |
samples, bytes |
su tamaño |
complete |
false cuando guarda menos de pre + post + 1 muestras: el anillo no llegaba tan atrás, o el agente se detuvo antes |
Desliza en horizontal para ver todas las columnas
La línea de disparo de /snapshot y /stream lleva id, cause, field
(se omite si está vacío), value, threshold, seq y wall_ns. El agente
guarda las 64 últimas.
DELETE /captures/{id}
Sección titulada «DELETE /captures/{id}»Libera los bytes de la captura y responde 204 sin cuerpo. Los ids
desconocidos o no numéricos responden igual que con GET.
POST /capture
Sección titulada «POST /capture»Arma una captura en la muestra más reciente, como si hubiera saltado una condición:
curl -X POST "http://172.30.10.2:9123/capture?reason=queue-tree-applied"reason vale operator por defecto. La respuesta es {"id":N,"armed":true}.
Si otra captura sigue recogiendo su ventana, o si una captura manual saltó
dentro de la ventana refractaria, la respuesta es 409 y no se arma nada. Las
condiciones, el presupuesto y lo que una captura no puede mostrar están en
Captura por disparo.