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 children attribute 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
    }
]