Tree API
This is the low-level API for interacting directly with the Arborist Manager. This document is intended for developers writing their own Arborist client(s). See the Ruby Client docs if you're just looking for information on the Ruby interface.
Overview
The Tree API serializes all data using msgpack for speed, and communicates on the wire via ZMQ. Both libraries enjoy rich support across many different languages, so you should be able to easily interact with Arborist using the language of your choice.
Nomenclature in this document matches that used by Msgpack. ie, a Map
is an associative array, Nil
is null, etc.
Connecting
Arborist uses a local ZMQ socket by default, at
ipc:///tmp/arborist_tree.sock
. If configured to listen on an external
address, you can connect to it from anywhere -- including broadcast
domains, multicast, UDP, or a more traditional TCP destination. (Future
features are planned to only allow trusted connections via ZMQ/ZAUTH.
For the time being, you'll have to rely on external firewalling to limit
access.)
Connections to the Manager API are of the ZMQ REQ
typed socket. Each
communication is a simple 1:1, request -> response. Other ZMQ socket
types will error. By default, the Manager only opens a local IPC
socket. Remote connections require Manager configuration changes, such
as:
---
arborist:
tree_api_url: tcp://10.3.0.75:5011
Packet Structure
Requests and responses are a msgpack Array of 2 elements -- a header, and a body.
[
header,
body
]
Request
Header
The header
is a Map that always has a minimum of two values:
key | required | type | description |
---|---|---|---|
action | yes | String | The Arborist command to execute. |
version | yes | Integer | The version of the API we're speaking. Currently, 1 . |
Additional key/value pairs are specific to the action
.
{
action: «verb», # required
version: 1, # required
[verb-specific attributes]
}
Body
The Body value for requests are dependant on the action
, but will
always be Nil, a Map, or an array of Maps. Omitting it in a request is
the same as sending a Nil value.
Response
Header
Response headers are similarly simple.
key | type | description |
---|---|---|
success | Boolean | Status of the request. |
version | Integer | The version of the API we're speaking. Currently, 1 . |
reason | String | Unsuccessful requests include the exception message here. |
category | String | Unsuccessful requests have a category of either server or client , meaning who is responsible for the error. |
{
success: true,
version: 1
}
{
success: false,
reason: "No such node 'blaaahhh'",
category: "client",
version: 1
}
Body
The Body value for responses are also dependant on the action
,
but will always be Nil, a Map, or an array of Maps. Omitting it in a
request is the same as sending a Nil value.
Actions
ack
Acknowledges a node. If the node is currently down
, the state
transitions to acked
. If the node is in any other state, the node
transitions to disabled
. Acking an already acked
node replaces the
ack data.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
identifier | yes | String | The identifier of the node to mark as acknowledged. |
Body arguments:
key | required | type | description |
---|---|---|---|
message | yes | String | A note for others to know why the node is acknowledged. |
sender | yes | String | The name/label of the acknowledgement source. |
via | no | String | An optional label of what mechanism was used for the ack, useful for reply or other tracking. |
[
{
action: "ack",
version: 1
identifier: «identifier»
},
{
message: "Looking into this."
sender: "mahlon",
via: "sms:5035551212"
}
]
Response
A regular response replies with an indication of success. There is no body.
[
{
success: true,
version: 1
}
]
deps
Display nodes that depend on the given node identifier.
Request
key | required | type | description |
---|---|---|---|
identifier | yes | String | The identifier of the node to search for dependants. |
[
{
action: "deps",
version: 1
identifier: «identifier»
}
]
Response
The response is a Map with a single key, deps
. The value is an Array
of dependant identifiers. If there are no dependants, the Array will be
empty.
[
{
success: true,
version: 1
},
{
deps: [ 'identifier-1', 'identifier-2', ... ]
}
]
fetch
Retrieve an Array of Maps that describes all or part of the node tree.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
from | no | String | The node identifier to start fetching from. Defaults to the root node. |
depth | no | Integer | Only returns children to this level. A depth of 0 returns only a single node. |
tree | no | Boolean | Populate the childrenattribute of each node, returning a structured dataset. |
[
{
action: "fetch",
version: 1
[from: «identifier»]
[depth: «arg»]
[tree: «boolean»]
}
]
Response
[
{
success: true,
version: 1
},
[
{
identifier: 'foo',
status: 'up',
parent: '_',
properties: {},
},
{
identifier: 'bar',
status: 'down',
parent: 'foo',
properties: {},
}
]
]
graft
Create a new node. Note: Changes to the running Manager won't survive daemon restarts, unless also reflected in startup configuration sources.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
identifier | yes | String | The node identifier to start fetching from. Defaults to the root node. |
type | yes | String | What kind of node? Common types are host , service , and resource . |
parent | no | String | Attach the node under this parent identifier. Defaults to the root node. |
The Body, if supplied, should be a Map of initial node operational attributes.
[
{
action: "graft",
version: 1
identifier: "hotsoup",
type: "host"
},
{
addresses: [ "10.1.67.2" ],
tags: [ "workstations" ]
}
]
Response
On a successful graft, the body is a Map with single key,
identifier
. The value is the newly saved node id.
[
{
success: true,
version: 1
},
{
identifier: "hotsoup"
}
]
modify
Alter operational attributes for a given node. Note: Changes to the running Manager won't survive daemon restarts, unless also reflected in startup configuration sources.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
identifier | yes | String | The node to modify. |
The Body should be a Map of node operational attributes to replace.
[
{
action: "modify",
version: 1,
identifier: "hotsoup"
},
{
addresses: [ "10.13.0.22" ]
}
]
Response
A successful response simple returns the standard header.
[
{
success: true,
version: 1
}
]
prune
Remove a node and all subordinates. Note: Changes to the running Manager won't survive daemon restarts, unless also reflected in startup configuration sources.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
identifier | yes | String | The node to remove. |
There is no body.
[
{
action: "prune",
version: 1,
identifier: "hotsoup"
}
]
Response
Successful responses return the serialized node as a Map. If the node
has subordinates you wish to retain, note that you'd need to fetch
them before calling prune
.
[
{
success: true,
version: 1
},
{
identifier: "hotsoup",
type: "host",
parent: "_",
description: "Mahlon's workstation",
tags: [ "workstation" ],
config: {},
status: "unknown",
properties: {},
ack: nil,
last_contacted: "1970-01-01T00:00:00+00:00",
status_changed: "1970-01-01T00:00:00+00:00",
errors: {},
dependencies: {behavior: "all", identifiers: [], subdeps: []},
quieted_reasons: {},
children: {},
addresses: ["10.3.0.75"]
}
]
search
Request
Header arguments:
key | value |
---|---|
exclude_down | Boolean. By default, all nodes are returned. If set, this option returns only nodes considered to be in a 'reachable' state (not in a status of down , disabled , or quieted .) |
return | An Array of properties to return. If unset, all properties are returned for matching nodes. If explicitly set to Nil, only node identifiers are returned. |
The body is a 2 element array, consisting of positive and negative search criteria. Search criteria is a Map of properties and their values you want to match on. If you don't pass a second Map of negative criteria, the body can instead be a single Map of positive criteria.
[
{
action: "search",
version: 1
[exclude_down: «boolean»]
[return: «array of properties | Nil»]
}
[
{ positive criteria }
{ negative criteria }
]
]
Fetch the address
, description
, and status
of nodes that are
currently down:
[
{
action: "search",
version: 1,
return: [ "address", "description", "status" ]
},
{
status: "down"
}
]
Response
Responses are a Map, keyed by node identifier.
{
"theon" => {
address: "10.2.10.4",
description: "no theon, reek",
status: "down",
},
"thoros" => {
address: "10.2.10.4",
description: "The Red God"s champion",
status: "down",
}
}
status
Fetch the status of the Manager.
Request
[
{
action: "status",
version: 1,
}
]
Response
[
{
success: true,
version: 1
},
{
server_version: 0.0.1,
state: "running",
uptime: 17155,
nodecount: 342
}
]
subscribe
Generate a subscription object for the given criteria, to be used as a key for the Event API.
Please note that this API is used for node events only. System event subscriptions are performed at the ZMQ socket level rather than the Manager's API.
key | value |
---|---|
event_type | String. Unknown event types are ignored and never match. If omitted, the subscription matches all event categories. |
identifier | String. A node id to listen to events on. Events propagate upwards through the tree. Omitting an identifier is a subscription to the root node, where you can catch all events. |
Event Types
Valid node events are outlined here.
Request
The body is a 2 element array, consisting of positive and negative search criteria. Search criteria is a Map of properties and their values you want to match on. If you don't pass a second Map of negative criteria, the body can instead be a single Map of positive criteria.
Get node change delta events for every 'host' type node.
[
{
action: "subscribe",
version: 1,
event_type: "node.delta"
},
{
type: 'host',
}
]
Get a snapshot of node state on every update for 'service' type nodes under the 'westside' node.
[
{
action: "subscribe",
version: 1,
event_type: "node.update",
identifier: "westside"
},
{
type: "service",
}
]
Get events of state changes to services running on port 80.
[
{
action: "subscribe",
version: 1,
event_type: "node.delta"
},
{
type: "service",
port: 80
}
]
Response
All successful responses return the subscription identifier, to be used with the Event API:
[
{
success: true,
version: 1
}
{
id: "68eb49e1-59e0-49d7-a5e9-343233fb408e"
}
]
unsubscribe
Remove a previously defined server-side subscription request.
Request
Header arguments:
key | required | type | description |
---|---|---|---|
subscription_id | yes | String | The UUID supplied during the subscription request. |
Response
Successful responses remove the subscription, and return the criteria originally used to create it.
[
{
success: true,
version: 1
},
{
event_type: "node.update",
criteria: {
type: "service",
port: 80
}
}
]
unack
Removes a prior acknowledgement from a node. This has no effect on a
node that isn't currently in one of the eacknowledged states (acked
,
disabled
.)
Request
Header arguments:
key | required | type | description |
---|---|---|---|
identifier | yes | String | The identifier of the node to mark as acknowledged. |
There is no body.
[
{
action: "unack",
version: 1
identifier: «identifier»
}
]
Response
A regular response replies with an indication of success. There is no body.
[
{
success: true,
version: 1
}
]
update
Update sets user property data for a set of nodes. Monitors can set arbitrary data for long term tracking and correlation of data.
Two special properties affect state transitions on nodes. A property
named error
, if set, may transition the node to down. A property
named warning
, if set, may transition the node to warn. If either
is missing or set to Nil, the node may transition to up.
See the Node Transitions for possible state changes.
Setting the value of any property to Nil effectively removes it. Unknown node identifiers are ignored.
A monitor_key
in the header effectively contains an error to a
specific monitor. If multiple monitors have set errors or warnings, all
must be cleared before the node can transition back to an up
state.
The monitor_key
defaults to _
if not explicitly supplied.
[
{
action: "update",
version: 1,
monitor_key: "icmp"
},
{
duir: {
pingtime: 0.02
},
sidonie: {
pingtime: 0.28
}
}
]
Updating the same nodes, with one in a failure state:
[
{
action: "update",
version: 1,
monitor_key: "icmp"
},
{
duir: {
pingtime: nil,
error: "Host unreachable."
},
sidonie: {
pingtime: 0.31
}
}
]
Response
[
{
success: true,
version: 1
}
]