Keen glossary
Every element of the collectors’ language, explained the same way: what it is for, what it takes, what it returns, and a few lines to see it at work.
The constructions
The instructions that decide and that repeat. Each one opens a block between braces, even for a single instruction.
if / elseif / else
if runs a block only when its condition is true. elseif adds another condition, judged if the earlier ones are false. else takes what is left. The conditions are judged in order, and only one block runs.
Write elseif as one word: else if is refused. else has no parameter: its block runs when no condition is true.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
if (condition) | an expression | yes | The condition of the first block. |
elseif (condition) | an expression | no (none), as many as needed | One more condition, judged only if the earlier ones are false. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | The instruction returns no value. It runs the chosen block, or none if no condition is true and there is no else. |
Examples
One case: stop the collector when the site answers anything but 200.
if ($answer['status'] != 200) {
fail('critical', 'Error response', 'Code ' . $answer['status']);
} Several cases and a rest: sort a response time into a class.
if ($answer['time'] > 2000) {
$class = 'slow';
} elseif ($answer['time'] > 500) {
$class = 'medium';
} else {
$class = 'fast';
} A condition made of several pieces, which reads the word only if there is one.
if ($word != '' && !text.contains($answer['body'], $word)) {
fail('critical', 'Word missing', 'The page does not contain the word ' . $word . '.');
} Faults the master reports
form_removed: A form removed from PHP:else ifis writtenelseif, and? :is written with anif.
Arrays: array(…) and $a['key']
An array is a series of cells, each with a label called its key. It is written array(…): a list (array('sshd', 'cron'), whose keys are 0, 1…), pairs (array('web' => 443)), or both.
A cell is read with $a['key'], or $a[0], or $a['key'][1] for an array inside an array. It is written with $a['key'] = …. Writing under a key that does not exist creates what is missing above it, but the variable itself must exist: create the array first with $a = array();.
An array is a value: assigning it to another variable, or passing it to a function, makes a copy. It keeps the order in which its keys came in, and foreach follows that order. A key is a text; an integer used as a key is worth its decimal writing, so $a[1] and $a['1'] are the same cell.
To add to a list, use list.add; to read a cell that may be missing, list.get.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
array(elements) | zero, one or more elements separated by commas, each value or key => value | no (array() is an empty array) | The content of the array. A comma after the last element is allowed. |
$a[key] | an expression: a text or an integer | yes | The cell to read or write. |
Returns
| What is returned | Form | When |
|---|---|---|
| The array | array | array(…) always returns an array. |
| The content of the cell | text, number, boolean or array | Reading $a[key] of a cell that exists. |
| Nothing | — | Writing $a[key] = … is an instruction. |
| A stop, state MISCONFIGURED | — | Reading a variable or a key that does not exist, reading inside something that is not an array, or writing inside something that is not an array. |
Examples
A list, then pairs.
$services = array('sshd', 'cron');
$ports = array('web' => 443, 'ssh' => 22); Read a cell by its label, then by its rank.
$port = $ports['web'];
$first = $services[0]; Write a cell in an array that holds an array: $read['eth0'] is created on the way.
$read = array();
$read['eth0']['mtu'] = 1500; Faults the master reports
form_removed: A form removed from PHP:[1, 2]is writtenarray(1, 2), and$a[] = $xis written$a = list.add($a, $x).variable_unassigned: A variable read without being assigned earlier in its scope. Writing a cell reads the variable: create the array before.
for
for repeats a block while counting. Between its parentheses, three parts separated by semicolons: the starting instruction, the condition to keep going, and the instruction run at the end of each turn.
Each part is a single piece: an assignment, a condition, an assignment. To step forward, write $i = $i + 1: $i++ does not exist.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
| start | an assignment, for example $i = 1 | yes | Run once, before the first turn. |
| condition | an expression | yes | Judged before each turn. The loop stops when it is false. |
| step | an assignment, for example $i = $i + 1 | yes | Run at the end of each turn. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | The instruction returns no value. |
| A stop, state MISCONFIGURED | — | The ceiling on turns is reached. The message names the ceiling and the line. |
Examples
Count from 1 to 8.
$total = 0;
for ($i = 1; $i <= 8; $i = $i + 1) {
$total = $total + $i;
} Walk the lines of an output, skipping the first one, which is the header.
$lines = text.lines($df['output']);
for ($i = 1; $i < list.count($lines); $i = $i + 1) {
$f = text.words($lines[$i]);
} Faults the master reports
form_removed: A form removed from PHP:++,--and+=are written$i = $i + 1.
foreach
foreach walks an array cell by cell, in the order in which its keys came in. At each turn, the content of the cell is put in a variable, and the block runs.
With a single variable, you receive the content. With two, written $key => $value, the first receives the cell’s label and the second its content.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
expression | an array: a variable, a function call | yes | What to walk through. |
as $value | a variable | yes | Receives the content of the cell at each turn. |
as $key => $value | two variables | no (a single variable) | The first receives the key of the cell, the second its content. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | The instruction returns no value. |
| A stop, state MISCONFIGURED | — | The ceiling on turns is reached. The message names the ceiling and the line. |
Examples
Walk the lines of a command’s output.
foreach (text.lines($stat['output']) as $line) {
$f = text.words($line);
} Take the key and the content: return one value per interface, named by the key.
foreach ($read as $name => $f) {
value('link', $f['operstate'], $name);
} while
while repeats a block as long as its condition is true. It is judged before each turn: if it is false from the start, the block never runs.
Unlike for, nothing counts for you: the block must change what the condition looks at. A loop never runs forever: the poller counts the turns and stops the collector at the ceiling.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
condition | an expression | yes | Judged before each turn. The loop stops when it is false. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | The instruction returns no value. |
| A stop, state MISCONFIGURED | — | The ceiling on turns is reached. The message names the ceiling and the line. |
Examples
Take one unit away at each turn until zero.
$left = 3;
while ($left > 0) {
$left = $left - 1;
} Wait three times two seconds.
$tries = 0;
while ($tries < 3) {
time.wait(2);
$tries = $tries + 1;
} Faults the master reports
form_removed: A form removed from PHP:dois writtenwhile.
break / continue
Two words to get out of a loop. break leaves it at once. continue drops the current turn and moves on to the next one.
They only apply inside a loop, and only to the one that contains them. In a function called from a loop, they are a fault.
Parameters
break and continue take no parameter, not even a number of loops to leave.
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | Neither returns a value. |
Examples
Look for a row in an array, then stop looking.
foreach ($interfaces as $row) {
if ($row['name'] == $wanted) {
$current = $row;
break;
}
} Move on to the next interface while a rate is not known.
foreach ($read as $name => $f) {
$in = counter.rate('in', $f['statistics/rx_bytes'] * 8, $name);
if ($in == '') {
continue;
}
value('traffic-in', number.round($in), $name);
} Faults the master reports
loop_word_outside_loop:breakorcontinueoutside a loop.
The basics of the language
What serves in almost every line: variables, texts, numbers, comparisons, comments and your own functions.
Variables and values
A variable is a box that has a name. Its name starts with $, followed by lowercase letters, digits or _, and does not start with a digit. It is born at its first assignment: there is no need to create it beforehand.
A value is one of four sorts: a text, a number (integer or decimal), a boolean (true or false), an array. There is no null: '', the empty text, says “nothing”. An optional variable with no value, an empty setting, a rate not known yet are all worth '', and the test is written if ($x != '').
An assignment is an instruction, never a value: if ($x = 1) and $a = $b = 1 are refused. A variable must be assigned earlier in the code before it is read.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | the sign $ and a name in lowercase ASCII, digits and _ | yes | The name of the variable. A capital letter is a fault. |
| value | an expression | yes | What is stored after the =. An array is copied, never shared. |
Returns
| What is returned | Form | When |
|---|---|---|
| The stored value | text, number, boolean or array | Each time $name is read. |
| A stop, state MISCONFIGURED | — | The variable is read while it does not exist. |
Examples
Store a text, an integer, a decimal and a boolean.
$host = 'www.example';
$retries = 3;
$ratio = 0.5;
$ready = true; Test an optional variable: with no value, it is worth the empty text.
$word = variable('web:word', 'text', array(
'label' => 'Word to find in the page',
'help' => 'Optional.',
'required' => false,
));
if ($word != '') {
// …
} Copy an array: changing $b does not touch $a, whose cell x is still 1.
$a = array('x' => 1);
$b = $a;
$b['x'] = 2; Faults the master reports
name_invalid: A name or a variable outside its form:$Total,$1,$$name.variable_unassigned: A variable read without being assigned earlier in its scope.
Texts and concatenation
A text is written between apostrophes or between double quotes, and can span several lines. Between apostrophes, \' is an apostrophe and \\ a backslash. Between double quotes, \n is a line break, \t a tab, \" a double quote. The sign $ never stands for a variable there: "$x" is the text $x.
The dot glues two values together. It is written with at least one blank on each side: $base . '/api'. A number is worth its writing. Without the blanks, it is a fault: a dot stuck to both names is reserved for calls, as in cache.get('token').
The dot comes after + and -: 'total ' . $a + $b reads 'total ' . ($a + $b).
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
left . right | two values, texts or numbers | yes | The two pieces to join. At least one blank on each side of the dot. |
Returns
| What is returned | Form | When |
|---|---|---|
| The joined text | text | Always. |
Examples
Add a path to an address.
$url = $base . '/api/v1/jobs'; Compose a sentence with numbers.
$state = $name . ': ' . $up . ' active member(s) out of ' . list.count($members); A text between double quotes, with a line break.
$detail = "Line 1\nLine 2"; Faults the master reports
concat_spacing: A concatenation dot with no blank on one side:$a .$b,'load:'.$r['output'].string_unterminated: A text between quotes that is not closed.character_invalid: A character outside the lexicon, outside a text: an accented letter, a non-breaking space.
Numbers and calculations
A number is an integer (0, 900) or a decimal with a dot (0.5, 14.3). A leading zero (017), a letter stuck to it (1e3, 0x1f) or two dots (1.5.3, an OID written without quotes) are refused. A negative number is written with the - of the calculation.
Five operators: +, -, *, / and %. Multiplication, division and remainder come before addition and subtraction, from left to right; parentheses force the order.
A calculation takes numbers, or texts that are the writing of a number: $fields[4] + $fields[5] adds two words read from a command’s output.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
left and right | a number, or a text that is the writing of a number | yes | The two sides of +, -, *, / and %. |
-value | a number, or a text that is the writing of a number | yes | The - placed in front of a value changes its sign. |
Returns
| What is returned | Form | When |
|---|---|---|
| The result | number | When both sides are numbers. |
| A stop, state UNREACHABLE | — | One side is a text that is not the writing of a number. |
| A stop, state MISCONFIGURED | — | A division by zero. |
Examples
A busy percentage, rounded to one decimal.
$busy = number.round(100 * (1 - $idle / $total), 1); Add two words of a line that was read.
$f = text.words($line);
$idle = $f[4] + $f[5]; Go from bits to bytes.
$bytes = $bits / 8; Faults the master reports
number_invalid: A number outside its form:017,1e3,0x1f,1.5.3.form_removed: A form removed from PHP:+=,-=,*=,/=,%=,++,--and**.
Comparisons and logical operators
== and != compare two values: as numbers when both sides are numbers, as texts otherwise. A number compared with '' is therefore always different: 0 != ''. <, >, <= and >= compare order.
== and != cannot be chained: $a == $b == $c is refused. Nor can the order comparisons: 1 < $x < 9 is refused. Write 1 < $x && $x < 9.
&& (and), || (or) and ! (not) combine conditions. && and || read their right side only if the left one does not already decide: $limit != '' && $x > $limit never compares $x with an empty setting.
From weakest to strongest, the priorities are: ||, &&, == !=, the order comparisons, the dot, + -, * / %, then ! and the - in front of a value. At equal priority, reading goes from left to right.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
== != | two values | yes | Equality and difference. Cannot be chained. |
< > <= >= | two values | yes | Order. Cannot be chained. |
&& || | two conditions | yes | And, or. The right side is read only if the left one does not already decide. |
! | a condition | yes | The opposite of the condition that follows. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | Always. The result of a comparison is never secret: you can compare a secret. |
Examples
Compare a response code.
if ($answer['status'] == 200) {
$health = list.get(json.read($answer['body']), 'status', 'absent');
} Compare with a threshold only when it is filled in.
if ($expected_speed != '' && $speed < $expected_speed) {
watch.raise('speed-degraded', 'warning', $name . ' negotiated at ' . $speed . ' Mbit/s', $name);
} Act when a key is missing.
if (!list.has($member_of, $name)) {
value('addresses', list.get($address_count, $name, 0), $name);
} Faults the master reports
form_removed: A form removed from PHP:===,!==,<>,<=>are written==or!=;and,or,xorare written&&and||.
Comments
A comment starts with //, outside a text between quotes, and runs to the end of the line. banshee ignores it: it is there only for whoever reads the code later.
It is the only form. # and /* … */ are refused.
Parameters
A comment takes no parameter: everything after // on the line is free text.
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A comment has no effect on the run. |
Examples
A comment line above what it explains.
// Ask the site for its page.
$answer = http.get($url); A comment at the end of a line.
$retries = 3; // Three tries at most. In a text between apostrophes, // does not open a comment.
$link = 'https://www.example/'; Faults the master reports
form_removed: A form removed from PHP:#and/* … */are written//.
Your functions: function and return
An author’s function stores instructions under a name, to use them several times. It is defined at the top level of the code, never inside a block or inside another function, before or after its calls.
It sees only its parameters: not the variables of the main body, nor global, nor static. return always returns a value and is written only in a function. No function calls itself, directly or through another: recursion is refused.
Its name is a bare name, with no dot. It cannot be a keyword, a domain name (ssh, snmp, http, json, text, number, list, counter, time, cache, secret, watch), or one of the seven collector words.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
name | a bare lowercase name, defined once | yes | The name of the function. |
$parameters | variables, all distinct, with no default value | no (none) | What the function receives. A call passes exactly as many values. |
return value | an expression | yes, in the function | The value the call returns. |
Returns
| What is returned | Form | When |
|---|---|---|
The value of return | text, number, boolean or array | At each call of the function. |
| A stop, state MISCONFIGURED | — | The ceiling on turns or on function calls is reached. |
Examples
A function that computes a percentage, and its call.
function percent($part, $total) {
return number.round(100 * $part / $total, 1);
}
$used = percent($f[2], $f[1]); A login to an API, which stops the collector if it is refused and returns the token otherwise.
function login($base, $user, $password) {
$body = json.write(array('username' => $user, 'password' => $password));
$answer = http.post($base . '/api/v1/login', $body, headers=array('Content-Type' => 'application/json'));
if ($answer['status'] != 200) {
fail('misconfigured', 'Login refused, code ' . $answer['status']);
}
$session = json.read($answer['body']);
return $session['access_token'];
} Faults the master reports
function_not_top_level: A function defined in a block or in a function.function_name_reserved: An author’s function under a reserved name.function_defined_twice: A function defined twice.parameter_twice: Two parameters with the same name.function_recursive: A function that calls itself, directly or through others.return_outside_function:returnoutside a function.
What does not exist in Keen
Keen looks like an old PHP, but several forms of PHP are removed from it, because they duplicate a form that is kept, read badly or open a door. Written in the code, outside a text between quotes and a comment, one of these forms is a fault that names it.
| Removed form | Write instead |
|---|---|
? : | if … else … |
+= -= *= /= %= .= | $i = $i + 1, $t = $t . 'x' |
++ -- | $i = $i + 1 |
$a[] = $x | $a = list.add($a, $x) |
[1, 2] | array(1, 2) |
=== !== <> <=> | ==, != |
and or xor | &&, || |
** | the calculation |
?? | list.get($a, $k, $default) |
else if | elseif |
null | a value, or '' for “nothing” |
echo print | value, fail, skip |
switch case default | if … elseif … |
do | while |
class new -> :: | functions |
global static | a parameter |
include include_once require require_once eval | nothing: a collector is a single code |
& | ^ ~ << >> | nothing: no reference, no binary calculation |
@ | nothing: a fault is not silenced |
| the backtick | nothing: no program is launched on the poller |
<?php <? ?> | nothing: the code starts directly |
# /* … */ | // |
: (the if (…): … endif; syntax) | braces |
A function that is not in this glossary does not exist: no file_get_contents, no exec, no preg_match, no isset. A collector does not write to a disk, does not open a connection outside the protocol functions, launches no program on the poller, and never writes a secret in clear.
Parameters
These forms take no parameter: the master refuses them.
Returns
| What is returned | Form | When |
|---|---|---|
A form_removed fault | a master fault | As soon as one of these forms is written. The master names it, with its line and column. |
A function_unknown fault | a master fault | As soon as a function that is not in this glossary is called. |
Examples
A test with two outcomes is written with if, never with ? :.
$label = 'no';
if ($answer['status'] == 200) {
$label = 'yes';
} To deliver a result, value takes the place of echo.
value('code', $answer['status']); Faults the master reports
form_removed: A removed form, with the form named.function_unknown: A function that does not exist:foo,snmp.set,watch.max.
The collector’s words
Seven words are written alone, without a domain, because they are the collector itself: need, variable, output, per, value, fail and skip.
The first four are declarations. They come at the top of the code, before any other instruction, and their arguments are written in clear: a number, a text, true, false, or an array(…) of those same things. The master reads them without running the collector. A declaration placed lower down, or mixed into an expression, is refused. No name is declared twice.
The options of a declaration are an array(…) of pairs. A key it does not take is a fault (option_unknown), so is a value outside its form (option_invalid), so is a required option that is absent (option_missing), and a key written twice is not judged further (option_twice).
need
need asks banshee for a standard variable, or for a protocol’s whole family, without redefining it: the host’s address, the SSH access, the SNMP version and community, the HTTP timeout. Their form, their secrecy and their validation are those of the variable in banshee.
A protocol function reads only the standard variables the collector has declared. A token set on a folder therefore never goes to a target the collector does not speak of.
A family declares at once everything its protocol reads:
| Family | Declares | Required |
|---|---|---|
protocol:ssh | host:address, protocol:ssh:user, protocol:ssh:fingerprint, protocol:ssh:port, protocol:ssh:timeout, protocol:ssh:password, protocol:ssh:private-key | host:address, protocol:ssh:user, protocol:ssh:fingerprint |
protocol:snmp | host:address, protocol:snmp:version, protocol:snmp:port, protocol:snmp:timeout, protocol:snmp:v2c:community, protocol:snmp:v3:user, protocol:snmp:v3:auth-protocol, protocol:snmp:v3:auth-password, protocol:snmp:v3:priv-protocol, protocol:snmp:v3:priv-password | host:address |
protocol:http | protocol:http:timeout, protocol:http:fingerprint, protocol:http:user-agent, then the eight variables of the TLS profile: protocol:tls:mode, protocol:tls:min-version, protocol:tls:max-version, protocol:tls:suites, protocol:tls:allow-incomplete-chain, protocol:tls:skip-verify, protocol:tls:authorities, protocol:tls:server-name | none |
protocol:ntp | protocol:ntp:server, protocol:ntp:timeout | protocol:ntp:server |
protocol:winrm | host:address, protocol:winrm:user, protocol:winrm:password, protocol:winrm:port, protocol:winrm:timeout, protocol:winrm:fingerprint, then the eight variables of the TLS profile, in the order of protocol:http | host:address, protocol:winrm:user, protocol:winrm:password |
protocol:icmp | host:address, protocol:icmp:count, protocol:icmp:timeout, protocol:icmp:interval | host:address |
protocol:dns | protocol:dns:server, protocol:dns:timeout | none |
protocol:tcp | host:address, protocol:tcp:timeout | host:address |
protocol:aws | protocol:aws:access-key-id, protocol:aws:secret-access-key, protocol:aws:session-token | protocol:aws:access-key-id, protocol:aws:secret-access-key |
protocol:ovh | protocol:ovh:application-key, protocol:ovh:application-secret, protocol:ovh:consumer-key | all three |
protocol:google | protocol:google:service-account-key | protocol:google:service-account-key |
protocol:ntp and protocol:dns do not declare host:address: ntp.time and dns.resolve ask their server, never the host. Without a value, protocol:dns:server is the resolver of the poller’s own system.
protocol:aws, protocol:ovh and protocol:google carry the keys of a cloud provider, and none declares host:address: a provider’s API is at the address the call writes. The address of the OVHcloud API, protocol:ovh:endpoint, is not in the family: you read it to write the address of the request, with its own need, next to the family.
The HTTP accesses (protocol:http:user, protocol:http:password, protocol:http:token) are in no family: the address of a request is free, and an access goes only to a collector that names it itself. Declare them one by one. The TLS profile is not an access: it carries no secret, and the family declares it at once.
An http.get, http.post, http.put or http.delete call requires the protocol:http family: without it, the probe would receive neither its TLS profile nor its User-Agent, and the connection would follow the “Standard” profile against the folder’s setting, silently. Likewise, ntp.time requires protocol:ntp, winrm.run protocol:winrm, icmp.ping protocol:icmp, dns.resolve protocol:dns and tcp.connect protocol:tcp: without its family, a call would ignore the server, the port, the count or the timeout the tree sets, silently. aws.sign, ovh.sign and google.token require protocol:aws, protocol:ovh and protocol:google: without it, they would have no key. Such a call is a fault (need_missing).
A variable is declared only once. host:address, which two families carry, is declared once when the code declares both; written by a need on top of a family that carries it, it is declared twice, and that is a fault.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a text written in clear: host:address, a protocol:… variable of the catalogue, or a family (protocol:ssh, protocol:snmp, protocol:http, protocol:ntp, protocol:winrm, protocol:icmp, protocol:dns, protocol:tcp, protocol:aws, protocol:ovh, protocol:google) | yes | The variable, or the whole family. Any other name is refused, a typing mistake as much as a reserved name. |
'optional' | the text optional | no (the variable is required) | Makes the variable optional. Refused on a family: each variable of a family is required or optional as the table says. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value of the variable for the probe’s host | text, number or boolean, depending on the variable’s form | When the need is assigned: $address = need('host:address');. An optional variable with no value returns ''. |
| Nothing | — | The need of a family, or written alone. |
| A stop, state MISCONFIGURED | — | A required variable has no value. |
Examples
Everything the SNMP protocol reads, in one line.
need('protocol:snmp'); An HTTP token, stored in a variable to put it in a header.
need('protocol:http');
$token = need('protocol:http:token');
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . $token)); An optional address.
$address = need('host:address', 'optional'); Faults the master reports
need_not_standard: Aneedthat names neither a standard variable nor a family:protocol:ssh:pasword,protocol:ftp.need_mode_invalid: The second argument is not'optional', or theneedof a family carries one.declared_twice: A name declared twice.declaration_misplaced: Aneedoutside the header, or used in an expression.need_missing: A protocol call without theneedof its family: anhttp.get,http.post,http.putorhttp.deletewithoutneed('protocol:http'), anicmp.pingwithoutneed('protocol:icmp').
variable
variable declares a setting of the collector’s own, which the administrator fills in on the probe. The collector’s author chooses its name, its form, its label and its help sentence.
Its name has 2 to 8 segments separated by : (web:url), in lowercase ASCII, digits, ., _ and -, and 255 characters at most. It cannot start with host, acl or protocol, which are reserved.
A secret setting (a password, a token) is declared with 'secret' => true and cannot have a default value, because a secret is not written in clear in a code.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a text written in clear, 2 to 8 segments separated by : | yes | The name of the setting. |
$form | text, multiline, integer, boolean or choice | yes | The form of the input. |
'label' | a non-empty text | yes, in $options | The label the screen shows. |
'help' | a non-empty text | yes, in $options | The sentence that explains the setting. |
'required' | true or false | no (true) | An optional setting with no value returns ''. |
'secret' | true or false | no (false) | Marks the setting as secret. |
'default' | a well-formed value for the form: a text, an integer for integer, true or false for boolean, one of the choices for choice | no (no default value) | Never for a secret. |
'inherit' | down, here or choice | no (choice) | The setting’s inheritance mode, as banshee defines it for its variables. |
'choices' | an array(…) of at least one text, each non-empty, all distinct | yes, and for choice only | The choices offered. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value of the setting for the probe’s host | a number for integer, true or false for boolean, a text for the other forms | When the declaration is assigned: $url = variable(…);. An optional setting with no value returns ''. |
| A stop, state MISCONFIGURED | — | A required setting has no value. |
Examples
An address to check.
$url = variable('web:url', 'text', array(
'label' => 'Address to check',
'help' => 'For example https://www.example/.',
)); A password, secret and with no default value.
$password = variable('backup:password', 'text', array(
'secret' => true,
'label' => 'Password',
'help' => 'The password of this account.',
)); A choice among a list.
$mode = variable('web:mode', 'choice', array(
'label' => 'Check method',
'help' => 'What the collector checks.',
'choices' => array('code', 'word'),
'default' => 'code',
)); Faults the master reports
variable_name_invalid: A name outside the form of a variable name.variable_family_reserved: A name of the familieshost,aclorprotocol.variable_form_invalid: A form outside the five.option_missing: A required option that is absent:labelorhelp.
output
output announces a value the collector is going to return, so that banshee can show it, keep it and watch it. The master reads it without running the code.
Each output without per must be returned once at every run by value. An output with per is returned once per instance.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a segment: lowercase ASCII, digits, ., _, -, 1 to 64 characters | yes | The name of the value, by which value and the watches find it: cpu-busy, load-1. |
$sort | number or text | yes | The sort of the value. |
'label' | a non-empty text | no (the screen shows the name) | The label the screen shows, for example “Incoming traffic”. |
'unit' | a non-empty text | no (none) | The unit: %, bit/s. |
'per' | the name of the sort of instances that the collector’s per declares | no (the value is single) | The value is returned once per instance. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. The value is returned with value. |
Examples
A single value, with its label and its unit.
output('response-time', 'number', array('label' => 'Response time', 'unit' => 'ms')); A value of sort text.
output('health', 'text', array('label' => 'Declared state')); A value returned once per interface.
per('interface', 'Interfaces');
output('traffic-in', 'number', array('label' => 'Incoming traffic', 'unit' => 'bit/s', 'per' => 'interface')); Faults the master reports
declared_name_invalid: The name of anoutputoutside the form of a segment.output_sort_invalid: A sort other thannumberandtext.per_undeclared: Anoutputwhoseperdoes not name the sort that the collector’sperdeclares.declared_twice: Anoutputname declared twice.
per
per declares the collector’s sort of instances, that is, what it measures several times per run: an interface, a core, a file system, a service. It also declares the default selection. A collector declares one at most.
The probe carries a single selection, for all its values and all its watches: All (except the patterns of ignore), including the instances that will appear later, or Only (the patterns of only). The administrator changes it on the probe. No watch has a filter of its own.
The code does not filter: it returns every instance it sees. It is the poller that sets aside, before judging or sending anything, those outside the selection.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a segment, like the name of an output | yes | The name of the sort of instances: interface, core, filesystem. |
$label | a non-empty text | yes | The word the screen shows for these instances, in the plural: “Interfaces”, “Cores”. |
'ignore' | an array(…) of at least one non-empty text, all distinct | no (none) | The patterns of the instances not to look at: the default selection is All except those. A pattern follows the rules of text.like. |
'only' | an array(…) of at least one non-empty text, all distinct | no (none) | The patterns of the only instances to look at: the default selection is Only those. Never with ignore. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. With neither ignore nor only, the default selection is All. |
Examples
The interfaces of a Linux, without the local interface or Docker’s.
per('interface', 'Interfaces', array('ignore' => array('lo', 'docker*', 'veth*'))); Two services looked at by default, the others chosen on the probe.
per('service', 'Services', array('only' => array('sshd', 'cron'))); Every core.
per('core', 'Cores'); Faults the master reports
per_twice: A secondper.declared_name_invalid: A name outside the form of a segment.title_invalid: A label that is not a non-empty text.option_invalid: An option outside its form, orignoreandonlytogether.
value
value returns a value announced by output. Its name is written in clear, and must be that of an output.
Each value is returned at most once per instance. A value is kept if the collector reaches its last instruction; if the collector stops through fail or by itself, no value goes out, not even those returned before the stop.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a text written in clear | yes | The name of a declared output. |
$value | an expression | yes | The value returned. |
$instance | an expression | yes if the output is declared per, refused otherwise | The name of the instance the value is about. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | value returns no value. |
| A stop, state MISCONFIGURED | — | A value that was announced is never returned, or is returned twice; or the value is a secret. |
| A stop, state UNREACHABLE | — | The value of an output of sort number is a text that is not the writing of a number. |
Examples
Return a site’s response code.
value('code', $answer['status']); Return a state per instance, here per interface.
value('link', $f['operstate'], $name); Return a value only when it is known.
if ($busy != '') {
value('core-busy', $busy, $f[0]);
} Faults the master reports
value_output_unknown: Avaluethat names nooutput.value_instance_required: Avaluewithout an instance for aperoutput.value_instance_forbidden: Avaluewith an instance for anoutputwithoutper.argument_not_literal: A name that is not written in clear.secret_written: A secret visible on reading, passed tovalue.
fail
fail stops the collector on a state that only it knows. The summary and the detail are the message that the probe shows.
A collector stopped by fail never delivers a value, not even those returned before, and no watch is judged: the probe takes the requested state, with the message.
Three states are possible: critical, misconfigured and unreachable.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$state | critical, misconfigured or unreachable, written in clear | yes | The state of the probe. |
$summary | a text | yes | The summary of the message. |
$detail | a text | no (none) | The detail of the message. |
Returns
| What is returned | Form | When |
|---|---|---|
| No value | — | fail stops the collector. Nothing comes after it. |
| The requested state, CRITICAL, MISCONFIGURED or UNREACHABLE | state of the probe, with the message | Each time fail runs. |
| A stop, state MISCONFIGURED | — | The summary or the detail contains a secret. |
Examples
Stop on a critical state when a word is missing.
fail('critical', 'Word missing', 'The page does not contain the word ' . $word . '.'); A command that fails on the target.
if ($df['exit'] != 0) {
fail('unreachable', 'df failed', $df['errors']);
} A badly configured probe.
if (!$found) {
fail('misconfigured', 'Interface not found: ' . $wanted);
} Faults the master reports
fail_state_invalid: A state outside the three.argument_not_literal: A state that is not written in clear.secret_written: A secret visible on reading, passed tofail.
skip
skip passes the turn: nothing goes out, and the probe keeps the state it had (PENDING if it has none yet). Use it when the collector cannot conclude at this run, for example at the first reading of a counter.
If the probe returns nothing any more, the master shows it OLD, and the reason of the last skip can be read in its detail.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$reason | a text | yes | Why the turn is passed. It is read in the probe’s detail. |
Returns
| What is returned | Form | When |
|---|---|---|
| No value | — | skip stops the collector delivering nothing. The probe keeps its state. |
| A stop, state MISCONFIGURED | — | The reason contains a secret. |
Examples
Wait for a second reading to compute a rate.
if ($in == '' || $out == '') {
skip('First reading, or counters went backwards: the rate will come at the next run');
} Wait for a busy value that is not known yet.
if ($global == '') {
skip("First reading: the busy value will come at the next run");
} Faults the master reports
secret_written: A secret visible on reading, passed toskip.
The watches
A collector measures and proposes its watches; the administrator chooses; the poller judges. A watch is a use case that the author writes once, with default thresholds: “Link down”, “Incoming saturation”. On a probe’s page, the administrator ticks the ones they want and sets their thresholds, without seeing a formula or any code. No threshold is written in the code itself.
A watch is declared in one line, in the header: the genre is the name of the method (watch.above), and the thresholds are its options. Nine genres are judged by banshee; a tenth, custom, is judged by the code itself. The sentences below are shown as the application writes them, in French.
| Genre | What it judges |
|---|---|
above | the value is strictly beyond a threshold |
below | the value is strictly under a threshold |
increase | a counter has risen too much over a window |
silence | the value has stayed at zero for too long |
habit | the value departs from its habit, at this hour and this day of the week |
deviate | the value of one instance departs from the other instances of the same run |
missing | expected instances are missing |
state | the value takes one of the states of a list |
expect | the value is none of those of a list |
custom | what the code judges itself, with watch.raise |
The settings of a watch each have a form:
- a number, written in clear, with or without a
-in front (70,0.5). Its unit is that of theoutput; - a duration, a text: an integer from 1 to 10,000 with no leading zero, a space, a unit.
s,min,handdgive a duration in time, 30 days at most.runorrunsgive a number of runs, allowed inforonly:'5 min','1 h','3 runs'; - a list, an
array(…)of at least one element, with no key written, each element a non-empty text (forstateandexpect, a text or a number), all distinct; - a text, in a
customonly: a non-empty text.
All thresholds are optional. With an empty threshold, its level is not judged: no warning, or no critical. A ticked watch with no threshold filled in never raises. window, sensitivity and side cannot be emptied.
After each run that reaches the last instruction, the poller judges. For each ticked watch and each instance the selection keeps, it raises a level, warning or critical, when its threshold is met. The critical level is judged before the warning, and a watch raises at most one level per instance, the most serious. The probe’s state is the most serious of all its raises, OK with none. The sentence of a raise is composed by banshee, except for a custom.
watch.above
watch.above raises when the value is strictly beyond a threshold: a processor or link load, a response time, a number of errors. At the exact threshold, nothing is raised.
With ahead, the value judged is the one the measure’s trend projects at that distance: “full within the week”. The trend needs a history of the measure; while it is being learned, the watch raises nothing and banshee shows it as learning.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a number | no (none) | The warning threshold, in the unit of the output. |
'critical' | a number | no (none) | The critical threshold. |
'for' | a duration, in runs or in time | no (1 run) | How long the condition must hold before it raises. In runs: at this run and the ones before. In time: for at least that long. A run where it does not hold starts it again from zero. |
'ahead' | a duration in time | no (no trend) | The distance at which the trend is projected. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The value exceeds the threshold for the duration of for. Example sentence: eth0 à 93 %, au-delà de 90 % depuis 5 min. |
Examples
An interface saturation, with two levels and a duration in time.
watch.above('saturation-in', 'Incoming saturation', 'in-percent', array('warning' => 70, 'critical' => 90, 'for' => '5 min')); A core saturated three runs in a row.
watch.above('core-hot', 'A saturated core', 'core-busy', array('warning' => 70, 'for' => '3 runs')); A disk that will be full within the week, from the trend.
watch.above('full-soon', 'Full within the week', 'used', array('warning' => 99, 'ahead' => '7 d')); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.below
watch.below raises when the value is strictly under a threshold: free space, an under-used core, a battery charge. It takes the same settings as watch.above, ahead included.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a number | no (none) | The warning threshold, in the unit of the output. |
'critical' | a number | no (none) | The critical threshold. |
'for' | a duration, in runs or in time | no (1 run) | How long the condition must hold before it raises. In runs: at this run and the ones before. In time: for at least that long. A run where it does not hold starts it again from zero. |
'ahead' | a duration in time | no (no trend) | The distance at which the trend is projected. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The value is under the threshold for the duration of for. Example sentence: cpu3 à 12 %, en dessous de 20 %. |
Examples
An under-used core.
watch.below('core-idle', 'An under-used core', 'core-busy', array('warning' => 20)); An interface with no IP address: critical when there are fewer than one.
watch.below('no-address', 'No more IP address', 'addresses', array('critical' => 1)); Low free space, with a watch that is unticked by default.
watch.below('free', 'Little free space', 'free', array('critical' => 1024, 'enabled' => false)); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.increase
watch.increase raises when a counter has risen too much over a window of time: link state changes, a cumulative number of errors. The output is a number that only grows.
The value judged is the current value minus the oldest one read in the window. A value smaller than the previous one, a counter reset to zero, starts the window again from it. While the window is not full, the increase is counted over what has been read.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a number | no (none) | The increase, over the window, that raises the warning. |
'critical' | a number | no (none) | The increase, over the window, that raises the critical level. |
'window' | a duration in time | no (1 h) | The length of the window. Cannot be emptied. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The increase over the window is beyond the threshold. Example sentence: eth0 : +5 en 1 h, au-delà de 3. |
Examples
An unstable link: more than three state changes in an hour.
watch.increase('link-flapping', 'Unstable link', 'carrier-changes', array('warning' => 3, 'window' => '1 h')); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.silence
watch.silence raises when the value has stayed at zero for too long: an interface with no packet at all, a service that no longer handles anything. The thresholds are durations.
The value must be zero at this run and at each of those before it, for at least the duration of the threshold.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a duration in time | no (none) | The time at zero that raises the warning. |
'critical' | a duration in time | no (none) | The time at zero that raises the critical level. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The value has been zero for the duration of the threshold. Example sentence: eth1 à zéro depuis 12 min. |
Examples
An interface silent for ten minutes, unticked by default.
watch.silence('mute', 'Silent interface', 'packets', array('warning' => '10 min', 'enabled' => false)); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.habit
watch.habit raises when the value departs from its habit for this probe, at this hour and this day of the week. It only ever raises the warning.
The habit needs the measure’s history. While it is being learned, the watch raises nothing and banshee shows it as learning.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'sensitivity' | low, normal or high | no (normal) | The sensitivity of the judgement of the departure. Cannot be emptied. |
'floor' | a number | no (none) | A floor: below this value, the watch never raises. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning raise, per instance | a level and a sentence | The value departs from its habit by more than the sensitivity allows, without being under the floor. Example sentence: eth0 à 840 Mbit/s, d’habitude 120 Mbit/s à cette heure. |
Examples
An unusual traffic, never raising under 10 Mbit/s, unticked by default.
watch.habit('traffic-unusual', 'Unusual traffic', 'traffic', array('floor' => 10000000, 'enabled' => false)); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.deviate
watch.deviate compares instances with each other: it raises when the value of one instance departs from the other instances of the same run, for example a core that is under-used while the others work. The output must be a number declared per.
The departure is measured against the median of the other instances’ values, that is, the middle value once they are sorted (the mean of the two middle ones when there is an even number). It must be strictly beyond the threshold. A run that keeps fewer than three instances of the output judges nothing and starts for again from zero.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a number, positive or zero | no (none) | The departure from the others’ median that raises the warning. A negative number is refused. |
'critical' | a number, positive or zero | no (none) | The departure that raises the critical level. |
'side' | below, above or both | no (both) | The side on which the departure counts: under the median, over it, or either. Cannot be emptied. |
'for' | a duration, in runs or in time | no (1 run) | How long the condition must hold before it raises. In runs: at this run and the ones before. In time: for at least that long. A run where it does not hold starts it again from zero. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The departure from the others’ median is beyond the threshold, on the right side, for the duration of for. Example sentence: cpu3 à 5 %, 75 % sous la médiane des autres (80 %). |
Examples
A neglected core while the others work.
watch.deviate('core-neglected', 'A neglected core', 'core-busy', array('side' => 'below', 'warning' => 30)); A core apart, on either side, with two levels.
watch.deviate('core-off', 'A core apart', 'core-busy', array('warning' => 20, 'critical' => 40)); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.missing
watch.missing raises when expected instances are missing: an interface that has disappeared, a file system that is no longer mounted. The output must be declared per, of any sort.
It judges on all the instances returned, selection or not. Each name in a level’s list that is not the instance of any value of the output at this run raises that level, once, with all the missing names.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a list of instance names | no (none) | The names whose absence raises the warning. |
'critical' | a list of instance names | no (none) | The names whose absence raises the critical level. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise | a level and a sentence | A name in the level’s list is no longer returned. Example sentence: absentes : eth0, bond0. |
Examples
Two interfaces to watch, critical if one is missing, unticked by default.
watch.missing('missing', 'Missing interface', 'link', array('critical' => array('eth0', 'bond0'), 'enabled' => false)); With no expected name, the watch raises nothing until the administrator enters one.
watch.missing('missing', 'Missing file system', 'used'); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.state
watch.state raises when the value takes one of the states of a list: a link down, a job failed. The output is a number or a text. A value is compared by its text writing.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a list of values | no (none) | The values that raise the warning. |
'critical' | a list of values | no (none) | The values that raise the critical level. |
'for' | a duration, in runs or in time | no (1 run) | How long the condition must hold before it raises. In runs: at this run and the ones before. In time: for at least that long. A run where it does not hold starts it again from zero. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The value is in the level’s list, for the duration of for. Example sentence: eth0 vaut down. |
Examples
A link that is down.
watch.state('link-down', 'Link down', 'link', array('critical' => array('down', 'lowerlayerdown'))); A backup job that failed.
watch.state('failed', 'Failed job', 'result', array('critical' => array('failed'))); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.expect
watch.expect is the opposite of watch.state: it raises when the value is none of those of a list. The list is that of the expected values, and any other value raises the level. The output is a number or a text.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
$output | the name of a declared output | yes | The measure being watched. The watch looks at every instance the code returns for it and the selection keeps. |
'warning' | a list of values | no (none) | The expected values; any other raises the warning. |
'critical' | a list of values | no (none) | The expected values; any other raises the critical level. |
'for' | a duration, in runs or in time | no (1 run) | How long the condition must hold before it raises. In runs: at this run and the ones before. In time: for at least that long. A run where it does not hold starts it again from zero. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
'help' | a non-empty text | no (none) | A sentence that says what the watch does, read under its title. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
A warning or critical raise, per instance | a level and a sentence | The value is in none of the level’s list, for the duration of for. Example sentence: eth0 vaut 1500, attendu : 9000. |
Examples
A response code that must be 200.
watch.expect('code', 'Error response', 'code', array('critical' => array(200))); A service that must be active.
watch.expect('stopped', 'Stopped service', 'state', array('critical' => array('active'))); A number of cores that the administrator fills in on the probe.
watch.expect('core-count', 'Unexpected number of cores', 'cores'); Faults the master reports
watch_output_unknown: A watch that names anoutputthat is not declared.watch_output_unfit: Anoutputwhose sort, or lack ofper, does not suit the genre.option_unknown: An options key that the declaration does not take.kind,output,messageandmatchno longer exist.option_invalid: An option outside its form.declared_twice: A name declared twice.
watch.custom
watch.custom declares a watch that the code judges itself, with watch.raise, for the cases no other genre covers. It has no output. Its settings, which the administrator changes on the probe, are read in the code with watch.setting.
The settings are declared under settings, an array(…) of pairs: the key is the name of the setting, the value an array(…) of pairs with the keys label, form, unit and default.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a segment: lowercase letters, digits, ., _, -, 1 to 64 characters | yes | The watch’s identifier. A probe finds what it ticked by it, from one version of the collector to the next. |
$title | a non-empty text | yes | The name the administrator reads. |
'help' | a non-empty text | yes | What the watch does, in one sentence: nothing else describes it. |
'settings' | an array(…) of pairs, setting name => its array(…) | no (no setting) | The settings the author declares. |
'label' | a non-empty text | yes, in each setting | What the administrator reads in front of the box. |
'form' | number, duration (a duration in time), names (a list) or text | yes, in each setting | The form of the input. |
'unit' | a non-empty text, for number only | no (none) | The unit displayed. |
'default' | a value of its form | no (empty) | The value the author proposes. |
'enabled' | true or false | no (true) | Ticked by default on a new probe. false suits a watch that needs a setting before it is of use. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | A declaration returns no value. |
| The code’s raises | a level and the sentence of watch.raise | The code calls watch.raise. The sentence is the one the code wrote, as it is. |
Examples
A negotiated speed under the expected speed, which the administrator sets on the probe.
watch.custom('speed-degraded', 'Degraded negotiated speed', array(
'help' => 'Warns when the negotiated speed is under the expected speed; critical when the interface is half-duplex.',
'settings' => array(
'expected' => array('label' => 'Expected speed', 'form' => 'number', 'unit' => 'Mbit/s', 'default' => 1000),
),
)); A degraded bond, with a single setting.
watch.custom('bond-degraded', 'Degraded bond', array(
'help' => 'Warns when a member of a bond goes down; critical when only a given number are left.',
'settings' => array(
'remaining' => array('label' => 'Critical if only this many are left', 'form' => 'number', 'default' => 1),
),
)); Faults the master reports
option_missing: A required option that is absent:helpof acustom, orlabelandformof a setting.option_invalid: An option outside its form, with the path of the key (settings.expected.form).option_unknown: A key that is neither a common option nor a known setting.declared_twice: A watch name declared twice.
watch.setting
watch.setting reads, in the code, a setting of a custom watch as the probe carries it, that is, as the administrator left it or changed it.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a text written in clear | yes | The identifier of a declared custom watch. |
$setting | a text written in clear | yes | The name of one of its settings. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value of the setting | a number for number, a number of seconds for duration, a list for names, a text for text | The setting has a value. |
'' | empty text | The setting is empty. |
Examples
Read an expected speed, and compare only if it is filled in.
$expected = watch.setting('speed-degraded', 'expected');
if ($expected != '' && $speed < $expected) {
watch.raise('speed-degraded', 'warning', $name . ' negotiated at ' . $speed . ' Mbit/s', $name);
} Faults the master reports
watch_unknown: A watch that is not declared.watch_not_custom: A watch of a genre other thancustom.watch_setting_unknown: A setting that the watch does not declare.argument_not_literal: An identifier or a setting that is not written in clear.
watch.raise
watch.raise raises a custom watch at a level, with a sentence. The raise is tied to an instance when the code gives one.
A raise of a watch the probe has not ticked, or of an instance outside the selection, is ignored. For a custom raised twice on the same instance, the most serious wins, and the first one at equal gravity.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$id | a text written in clear | yes | The identifier of a declared custom watch. |
$state | warning or critical, written in clear | yes | The level raised. |
$message | a text | yes | The sentence that says why. |
$instance | a text | no (none) | The instance the raise is about. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | watch.raise returns no value. |
| A stop, state MISCONFIGURED | — | The message contains a secret. |
Examples
Raise a warning on an interface.
watch.raise('speed-degraded', 'warning', $name . ' negotiated at ' . $speed . ' Mbit/s', $name); Raise the critical level for a half-duplex.
if ($f['duplex'] == 'half') {
watch.raise('speed-degraded', 'critical', $name . ' in half-duplex', $name);
} Faults the master reports
watch_unknown: A watch that is not declared.watch_not_custom: A watch of a genre other thancustom.watch_state_invalid: A state other thanwarningandcritical.secret_written: A secret visible on reading, passed towatch.raise.
The protocols
A function is written domain.method, in English, with no blank around the dot. The first argument of a function is always what it acts on. A protocol call takes what it acts on as positional arguments, and its details as named arguments, all optional. A number of positional arguments outside those the function takes is a fault (argument_count).
ssh, snmp, tls, winrm, icmp and tcp reach the host’s address, host:address; http reaches the address the call writes; google.token, Google’s token server; ntp and dns ask their server, never the host. Each reads its accesses in the standard variables of its protocol that the collector declares with need. The connection is set in the tree, never in the code: the port, the timeout, the SSH host key, the TLS profile and the fingerprint of a certificate are variables the administrator sets on a folder, a host or a probe. The call only says what the request asks of the target. Only the tls functions take their settings as arguments, since they test what a server accepts.
A call that fails stops the collector, in the state the table of failures gives. To decide yourself, ask the call for it with errors=$err. An HTTP code 500 is an answer, which is returned as a value.
Named arguments: name=value
A named argument is written name=value, in the list of a call’s arguments, after its positional arguments, in any order you like: http.get($url, headers=array('Accept' => 'application/json'), errors=$err). The positional arguments say what the call acts on: the address, the command, the OID. The named arguments give its details, and all of them are optional.
Only the protocol functions take them, each its own; the tools, your functions and the declarations take none. There is only one form: a detail is never passed as a positional argument, and http.get($url, $headers) is refused.
| Function | Named arguments |
|---|---|
ssh.run | arguments=, errors= |
winrm.run | errors= |
snmp.get, snmp.walk, snmp.table, snmp.date | errors= |
http.get, http.delete | headers=, user=, password=, errors= |
http.post, http.put | headers=, user=, password=, public=, errors= |
tls.inspect | name=, authorities=, preamble=, errors= |
tls.try | name=, preamble=, errors= |
ntp.time, icmp.ping, dns.resolve, tcp.connect, google.token | errors= |
In a list of arguments, a name followed by a single = always opens a named argument; an argument written $a == 1 remains a comparison. Outside a list of arguments, = only makes an assignment.
A value written out in full is judged as soon as the code is validated. A computed value is judged when the collector runs, and the same fault then stops the collector in the state MISCONFIGURED.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
name | one of the names the function takes, once per call | yes | The detail the argument gives. |
value | an expression, in the form the name asks for | yes | The value of the detail. |
Returns
| What is returned | Form | When |
|---|---|---|
| What the call returns | that of the function | A named argument does not change what the call returns. errors= also fills the variable it names. |
Examples
A header, then the failure caught, after the address.
$answer = http.get($url, headers=array('Accept' => 'application/json'), errors=$err); Named arguments go in any order you like: these two calls make the same request.
$first = http.get($url, user=$user, password=$password);
$second = http.get($url, password=$password, user=$user); The arguments of an SSH command, each one sent separately and protected.
$units = ssh.run('systemctl is-active', arguments=array('nginx', 'sshd')); Faults the master reports
argument_count: A number of positional arguments outside those the function takes. The old formhttp.get($url, $headers)is one.named_argument_unknown: A name the function does not take:timeout=onhttp.get, or any name on a tool.named_argument_twice: The same name twice in a call.positional_after_named: A positional argument placed after a named argument.named_argument_missing:user=withoutpassword=, or the other way round: they go together.named_argument_invalid: A value written out in full outside its form:arguments=that is not a list,headers=that is not anarray(…)of pairs, anAuthorizationheader next touser=, an account that contains:.errors_not_variable:errors=followed by something other than a lone variable:errors='x',errors=$e['k'].
errors=: catching the failure of a call
By default, a protocol call that fails stops the collector, in the state the table below gives. The probe’s message carries the code, the text and the details of the failure, and its line. A collector that asked for nothing therefore never receives an empty result: an snmp.table that fails does not return an empty table that a foreach would walk through without returning anything.
To decide yourself, ask the call for it with errors=$err, a variable you name. The error is not mixed into the result: the call returns what it always returns, and fills the variable, whether it succeeds or not, with an array of three keys, code, text and details. On a failure it catches, the call returns '' and the collector goes on. Test the failure with $err['code'] != ''.
errors= holds for its own call only: there is no try and no catch, and the rest of the code reads as always. It counts as an assignment of the variable, which it replaces whole at each call. A collector that writes it has chosen to test the failure: reading $answer['status'] from a call that failed, which returned '', stops the collector in the state MISCONFIGURED.
Some failures are never caught and always stop the collector: a refusal by the check of the connection (the TLS profile, the fingerprint of a certificate, the SSH host key), which the tree sets and the code does not touch; a fault of the code or of the configuration found before anything is sent, such as a secret sent to an http:// address, a poller without the right to send ICMP, or an unreadable Google service account key; a ceiling of the poller, a response that is too big included; a runtime error.
The table of failures:
| Code | When | details | Calls | State without errors= | Caught by errors= |
|---|---|---|---|---|---|
timeout | The target does not answer within the timeout of its protocol. In SNMP v2c, a wrong community shows this way: the agent stays silent. | seconds | all but icmp.ping and tcp.connect, for which silence is an answer | UNREACHABLE | yes |
connection_refused | The target refuses the connection: no service on that port. | — | all but icmp.ping and tcp.connect, for which a refusal is an answer | UNREACHABLE | yes |
host_unknown | The name of the address does not resolve; for dns.resolve, the name of its server, since the name asked always gets an answer (NXDOMAIN). | — | all | UNREACHABLE | yes |
network_error | Any other breakdown between the poller and the target: no route, a connection cut, an answer the protocol cannot read. | — | all | UNREACHABLE | yes |
access_denied | The target refuses access: an SSH account refused, an SNMP v3 user or password refused, an OID the agent forbids, an account the WinRM service refuses, a key or a scope Google’s token server refuses. | — | ssh.run, snmp.*, winrm.run, google.token | MISCONFIGURED | yes |
snmp_not_found | The agent does not have that OID. | — | snmp.get, snmp.date | MISCONFIGURED | yes |
snmp_date_invalid | The value is not an instant: not a string of 11 bytes whose fields keep within the bounds of RFC 2579 (another syntax, another length, a month 13). A string of 8 bytes, without its offset from UTC, is one too: the instant cannot be guessed. | — | snmp.date | MISCONFIGURED | yes |
snmp_error | The agent answers with another error. | status: its name (genErr, tooBig…) | snmp.* | UNREACHABLE | yes |
ntp_answer_invalid | The time server answers without giving a sure time: a refusal (kiss-of-death, stratum 0), a server that says it is not synchronised (stratum 16, or a leap indicator at 3), an answer that is not a server’s to this request. | — | ntp.time | UNREACHABLE | yes |
icmp_not_permitted | The poller has no right to send ICMP, found before anything is sent: a fault of the poller’s configuration, which the author does not fix. | — | icmp.ping | MISCONFIGURED | no |
ssh_handshake_failed | The SSH handshake fails: no common algorithm, or the server closes. | — | ssh.run | UNREACHABLE | yes |
winrm_error | The WinRM service refuses the request with a WS-Management fault, other than an account refused: a shell it does not open, a quota reached. | fault: the code of the fault, as the service writes it | winrm.run | UNREACHABLE | yes |
tls_handshake_failed | The TLS handshake fails without a refusal of the profile to explain it: the server does not speak TLS on that port, or nothing the poller knows. For tls.try, a server that refuses the handshake gives an answer, not a failure. | alert | http.* over https://, tls.*, winrm.run, google.token | UNREACHABLE | yes |
tls_version_refused, tls_suite_refused, tls_certificate_expired, tls_certificate_not_yet_valid, tls_certificate_name_mismatch, tls_certificate_untrusted, tls_certificate_invalid | The refusals of the probe’s TLS profile; for google.token, of the “Standard” profile. | their parameters | http.* over https://, winrm.run, google.token | MISCONFIGURED | no |
fingerprint_mismatch | The fingerprint presented, of an SSH host key or of a certificate, is not the one pinned in the variable of its protocol (protocol:ssh:fingerprint, protocol:http:fingerprint, protocol:winrm:fingerprint). | expected, found | ssh.run, http.* over https://, winrm.run | MISCONFIGURED | no |
fingerprint_changed | The fingerprint presented is no longer the one the probe retained for this host and port, and no update replaces it. | expected, found | ssh.run, http.* over https://, winrm.run | MISCONFIGURED | no |
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
errors=$err | a lone variable | no (the failure stops the collector) | The variable the call fills, whether it succeeds or not. |
Returns
| What is returned | Form | When |
|---|---|---|
$err['code'] | text | '' when the call succeeds; otherwise the code of the failure, one of those of the table. |
$err['text'] | text | '' when the call succeeds; otherwise the text of the failure, in English, as the protocol’s library gives it. It is there to be shown; to decide, read the code. |
$err['details'] | array | array() when the call succeeds; otherwise the details the table gives its code. |
'', returned by the call | empty text | The call failed and errors= caught the failure. |
Examples
A site that does not answer is down: the collector says it is critical, rather than leaving the probe unreachable.
$answer = http.get($url, errors=$err);
if ($err['code'] == 'connection_refused' || $err['code'] == 'timeout') {
fail('critical', 'Site down', $err['text']);
}
if ($err['code'] != '') {
fail('unreachable', 'Site unreachable', $err['text']);
}
value('code', $answer['status']); An OID the agent does not have, replaced by a text.
$name = snmp.get('1.3.6.1.2.1.1.5.0', errors=$err);
if ($err['code'] == 'snmp_not_found') {
$name = 'unknown';
} Faults the master reports
errors_not_variable:errors=followed by something other than a lone variable.named_argument_twice:errors=written twice in a call.
ssh.run
ssh.run runs a command on the target, over SSH, and returns its output, its error messages and its exit code. The fingerprint of the host key (protocol:ssh:fingerprint) is checked before any credential is sent.
What the command does on the target is left to the judgement of whoever writes the collector. banshee reads targets; reading the diff on the poller is the safeguard.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$command | a text | yes | The command to run. |
arguments= | a list of texts | no (none) | The arguments of the command. Each one is sent separately and protected, never glued into the command. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The output, the errors and the exit code | array('output' => text, 'errors' => text, 'exit' => number) | The command ran, whatever its exit code. |
| A stop, state UNREACHABLE | — | No answer from the target, or an SSH handshake that fails: timeout, connection_refused, host_unknown, network_error, ssh_handshake_failed. |
| A stop, state MISCONFIGURED | — | Accesses refused (access_denied), or a fingerprint that is not the one expected (fingerprint_mismatch, fingerprint_changed, never caught). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Read the processor counters of a Linux.
need('protocol:ssh');
$stat = ssh.run('grep ^cpu /proc/stat'); Check the exit code before reading the output.
$df = ssh.run('df -P -k');
if ($df['exit'] != 0) {
fail('unreachable', 'df failed', $df['errors']);
} Pass an argument apart from the command.
$load = ssh.run('cat', arguments=array('/proc/loadavg')); winrm.run
winrm.run runs a PowerShell script on the target, over WinRM, and returns its output, its error messages and its exit code, the same way as ssh.run. WinRM starts PowerShell itself: the script is written as it is, without powershell -Command around it.
WinRM goes over HTTPS only, on the port of protocol:winrm:port (5986 by default). The connection follows the probe’s TLS profile, like an https:// request, and the certificate is judged, or its fingerprint pinned in protocol:winrm:fingerprint checked, before any credential is sent. The account is that of protocol:winrm:user and protocol:winrm:password, by NTLM: the password never leaves, only an answer computed with it.
The call requires the need('protocol:winrm') family. What the script does on the target is left to the judgement of whoever writes the collector, as for an SSH command.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$script | a text | yes | The PowerShell script to run. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The output, the errors and the exit code | array('output' => text, 'errors' => text, 'exit' => number) | The script ran, whatever its exit code. |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), a TLS handshake that fails (tls_handshake_failed), or a fault of the service (winrm_error). |
| A stop, state MISCONFIGURED | — | An account refused (access_denied), a refusal of the TLS profile, or a fingerprint that is not the one expected (fingerprint_mismatch, fingerprint_changed): those last two kinds are never caught. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Read the date of a Windows.
need('protocol:winrm');
$date = winrm.run('Get-Date -Format o'); Check the exit code before reading the output.
$read = winrm.run('Get-Service -Name Spooler | Select-Object -ExpandProperty Status');
if ($read['exit'] != 0) {
fail('unreachable', 'PowerShell failed', $read['errors']);
} Faults the master reports
need_missing: A call withoutneed('protocol:winrm')in the header.named_argument_unknown:arguments=, whichwinrm.rundoes not take: write the values in the script.
snmp.get
snmp.get reads an OID, the address of a piece of information in a device, and returns its value. The OID is written between apostrophes: without them, 1.3.6.1.2.1.1.5.0 is a malformed number.
A string of octets is returned as text when its octets are a printable text. Otherwise, binary like a MAC address or written in another encoding than UTF-8, it is returned as hex: followed by its octets in lower-case hexadecimal: hex:001a2b3c4d5e. A value that starts with hex: is always hexadecimal: a printable text that starts so is written in hexadecimal too. A binary string whose every octet happens to be printable is returned as text, and hex.encode gives back its octets.
Reading is all there is: there is no snmp.set.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$oid | a text | yes | The OID to read. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value read | the value of the OID | The device answers. |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), or another error from the agent (snmp_error). |
| A stop, state MISCONFIGURED | — | Accesses refused (access_denied), or an OID the agent does not have (snmp_not_found). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Read a device’s name.
need('protocol:snmp');
value('system-name', snmp.get('1.3.6.1.2.1.1.5.0')); Read the time since boot, in hundredths of a second, and convert it to days.
value('uptime', number.round(snmp.get('1.3.6.1.2.1.1.3.0') / 8640000, 1)); Read a MAC address, always in hexadecimal.
$mac = snmp.get('1.3.6.1.2.1.2.2.1.6.1');
if (text.like($mac, 'hex:*')) {
$parts = text.split($mac, ':');
$mac = $parts[1];
} else {
$mac = hex.encode($mac);
} Faults the master reports
function_unknown: A function that does not exist:snmp.set.
snmp.walk
snmp.walk reads a whole branch of the SNMP tree and returns its values in an array, filed under the end of their OID.
Each value follows the rule of snmp.get: a string of octets that is not a printable text is returned as hex: and its hexadecimal.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$oid | a text | yes | The OID of the branch to read. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The values of the branch | array(suffix => value) | The device answers. An empty branch is an answer: array(). |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), another error from the agent (snmp_error), or a table that is too long. |
| A stop, state MISCONFIGURED | — | Accesses refused (access_denied). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Read the names of all the interfaces and walk through the result.
$names = snmp.walk('1.3.6.1.2.1.31.1.1.1.1');
foreach ($names as $index => $name) {
// $index is the suffix of the OID, $name its value.
} snmp.table
snmp.table reads several columns of a device and matches them by their index, so that each row gathers what concerns the same object. It is the shortest way to read, for example, the name, the speed and the state of each interface.
Each value follows the rule of snmp.get: a string of octets that is not a printable text is returned as hex: and its hexadecimal.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$columns | an array(column => oid, …) | yes | The columns to read: the name you give each one, and its OID. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The rows of the table | array(index => array(column => value)) | One row per index present in at least one column. A column that is absent for an index is not in its row. |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), another error from the agent (snmp_error), or a table that is too long. |
| A stop, state MISCONFIGURED | — | Accesses refused (access_denied). |
'' | empty text | The call failed and errors= caught the failure. Without errors=, a failure never returns an empty table. |
Examples
Read the name, the speed and the state of each interface.
$interfaces = snmp.table(array(
'name' => '1.3.6.1.2.1.31.1.1.1.1',
'speed' => '1.3.6.1.2.1.31.1.1.1.15',
'status' => '1.3.6.1.2.1.2.2.1.8',
)); Return one value per interface, named by the name column.
foreach ($interfaces as $index => $row) {
value('status', $row['status'], $row['name']);
value('speed', $row['speed'], $row['name']);
} snmp.date
snmp.date reads, like snmp.get, an OID of the DateAndTime syntax (RFC 2579), such as the system time of a host, and brings it back to UTC by the offset the value carries. It returns an instant, in seconds since 1 January 1970 UTC, to the tenth: the form of time.now, so that the two subtract.
A value of 8 bytes, without its offset from UTC, is a failure (snmp_date_invalid): the RFC says it is in the agent’s local time, without saying which, and taking it for UTC would give an instant wrong by the whole time zone, silently.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$oid | a text | yes | The OID to read, of the DateAndTime syntax. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The instant | a number, in seconds since 1 January 1970 UTC, to the tenth | The device answers with a DateAndTime of 11 bytes. |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), or another error from the agent (snmp_error). |
| A stop, state MISCONFIGURED | — | Accesses refused (access_denied), an OID the agent does not have (snmp_not_found), or a value that is not an instant (snmp_date_invalid). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
The system time of a host, hrSystemDate.
need('protocol:snmp');
$date = snmp.date('1.3.6.1.2.1.25.1.2.0'); Faults the master reports
argument_count:snmp.date(), without an OID.
http.get
http.get sends a GET request and returns the response code, the headers, the body and the response time. Only http:// and https:// addresses are allowed, and no redirect is followed: a 3xx response is returned as it is, and the address it points to is read in headers, under location.
An https:// connection follows the probe’s TLS profile, which the administrator sets in the tree: its versions, its suites and the judgement of the certificate, or the pinned fingerprint (protocol:http:fingerprint). A connection the profile refuses stops the collector. A 500 code is an answer, not a stop.
The call requires the need('protocol:http') family, which brings it the probe’s timeout, fingerprint, User-Agent and TLS profile: without it, it is a fault (need_missing).
The headers (headers=) are sent as you write them. The type of a body is the Content-Type header you write. A name or a value outside the form of a header, such as a line break in a value, stops the collector.
The User-Agent is the value of the probe’s protocol:http:user-agent, banshee/ followed by the poller’s version when it has no value; a User-Agent header written in the call wins. A Host header written in the call is the site the request asks for, for a server with several sites reached by its IP address: it changes neither the address reached nor the name the TLS handshake announces and judges, which are those of the address or of protocol:tls:server-name.
user= and password= send an account in Basic, in the Authorization header that http composes. One does not go without the other, never with an Authorization header written in headers=, and the account does not contain :. A user= that is '' sends no account: a collector whose account is optional writes the call once.
http adds no access by itself: an account or a token is sent only if the call writes it. The response to a request whose body carried a secret is secret, except its code and its time; a secret passed in a header does not, by itself, make the response secret. But any part of a response that holds a secret of the probe, its text, its base64 or its address encoding, is secret, whatever the request carried: see secret.mark.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$url | a text, starting with http:// or https:// | yes | The address to query. |
headers= | array(name => value) | no (none) | The request’s headers. |
user= | a text without : | no (no account), with password= | The account sent in Basic. '' sends no account. |
password= | a text | no (no account), with user= | The password of that account. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The response | array('status' => number, 'headers' => array(name => value), 'body' => text, 'time' => number) | The server answers, whatever its code. Header names are lowercase, a repeated header is joined with , , the time is in milliseconds. |
| A stop, state UNREACHABLE | — | No answer from the target (timeout, connection_refused, host_unknown, network_error), a TLS handshake that fails (tls_handshake_failed), or a response that is too big. |
| A stop, state MISCONFIGURED | — | The probe’s TLS profile refuses the connection (never caught), a secret goes in an http:// request, or a header is outside its form. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Ask for a page and read its code and its time.
need('protocol:http');
$answer = http.get('https://www.example/');
value('code', $answer['status']);
value('response-time', $answer['time']); Send a token in a header.
need('protocol:http');
$token = need('protocol:http:token');
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . $token)); Send an optional account: with no account set on the probe, the request goes without one.
need('protocol:http');
$user = need('protocol:http:user', 'optional');
$password = need('protocol:http:password', 'optional');
$answer = http.get($url, user=$user, password=$password); Read the address the site redirects to.
need('protocol:http');
$answer = http.get($url);
if ($answer['status'] == 301) {
$target = $answer['headers']['location'];
} Faults the master reports
need_missing: A call withoutneed('protocol:http')in the header.named_argument_missing:user=withoutpassword=, or the other way round.named_argument_invalid: AnAuthorizationheader next touser=, whatever its case, an account that contains:, orheaders=that is not anarray(…)of pairs.
http.post
http.post sends a POST request, whose body is a text, and returns the same response as http.get. The same rules apply: http:// and https:// only, no redirect followed, no access added by http, the need('protocol:http') family required.
What the request does on the target is left to the judgement of whoever writes the collector. There are four HTTP methods, and no other: http.get, http.post, http.put and http.delete.
The response to a request whose body carries a secret is secret. Some APIs take their key in the body only, and do not return it: public=true then declares that the response carries no token, and it reads like any other. Whatever holds a secret of the probe stays secret despite the declaration. Declaring the response of a login free of tokens exposes the token it returns: that is your judgement, and the documentation of the collector says why the response carries none.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$url | a text, starting with http:// or https:// | yes | The address to query. |
$body | a text | yes | The body of the request. |
headers=, user=, password=, errors= | as for http.get | no | As for http.get. |
public= | true or false, written in the clear | no (false) | Declares that the response to a body carrying a secret carries no token. |
Returns
| What is returned | Form | When |
|---|---|---|
| The response | array('status' => number, 'headers' => array(name => value), 'body' => text, 'time' => number) | As for http.get. |
| A stop, state UNREACHABLE or MISCONFIGURED | — | As for http.get. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Log in to an API with an account, and read the token returned.
$body = json.write(array('username' => $user, 'password' => $password));
$answer = http.post($base . '/api/v1/login', $body, headers=array('Content-Type' => 'application/json'));
$session = json.read($answer['body']); Read an API that takes its key in the body only, and whose response carries no token.
need('protocol:http');
$key = need('protocol:http:token');
$answer = http.post('https://api.example/info', json.write(array('key' => $key)),
headers=array('Content-Type' => 'application/json'), public=true);
$info = json.read($answer['body']); Faults the master reports
need_missing: A call withoutneed('protocol:http')in the header.named_argument_invalid:public=that is nottrueorfalsewritten in the clear, a computed value included.
http.put
http.put sends a PUT request, whose body is a text, and returns the same response as http.get. The same rules apply: http:// and https:// only, no redirect followed, no access added by http, the need('protocol:http') family required.
What the request does on the target is left to the judgement of whoever writes the collector, as for http.post. Some APIs open a session with a PUT, and close it with an http.delete.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$url | a text, starting with http:// or https:// | yes | The address to query. |
$body | a text | yes | The body of the request. |
headers=, user=, password=, errors= | as for http.get | no | As for http.get. |
public= | as for http.post | no (false) | As for http.post. |
Returns
| What is returned | Form | When |
|---|---|---|
| The response | array('status' => number, 'headers' => array(name => value), 'body' => text, 'time' => number) | As for http.get. |
| A stop, state UNREACHABLE or MISCONFIGURED | — | As for http.get. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Open a session, and read the token returned.
$body = json.write(array('username' => $user, 'password' => $password));
$answer = http.put($base . '/api/session', $body, headers=array('Content-Type' => 'application/json'));
$session = json.read($answer['body']); Faults the master reports
need_missing: A call withoutneed('protocol:http')in the header.argument_count:http.put($url), without a body.
http.delete
http.delete sends a DELETE request, without a body, and returns the same response as http.get. The same rules apply: http:// and https:// only, no redirect followed, no access added by http, the need('protocol:http') family required.
What the request does on the target is left to the judgement of whoever writes the collector, as for http.post. Its first use is to close the session a collector opened, so that a run leaves nothing behind on the target.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$url | a text, starting with http:// or https:// | yes | The address to query. |
headers=, user=, password=, errors= | as for http.get | no | As for http.get. |
Returns
| What is returned | Form | When |
|---|---|---|
| The response | array('status' => number, 'headers' => array(name => value), 'body' => text, 'time' => number) | As for http.get. |
| A stop, state UNREACHABLE or MISCONFIGURED | — | As for http.get. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Close the session the collector opened, without stopping it if the closing fails.
$closed = http.delete($base . '/api/session', headers=array('Authorization' => 'Bearer ' . $session['token']), errors=$err); Faults the master reports
need_missing: A call withoutneed('protocol:http')in the header.argument_count:http.delete($url, $body): a DELETE request has no body.
tls.inspect
tls.inspect makes a TLS handshake to the host’s address, host:address, on the given port, without sending anything else. It offers everything the poller speaks, reads the certificate presented and judges it without ever refusing: it returns what it found.
It ignores the inherited TLS profile: it reads no protocol:tls variable, and its arguments say what it tries. An empty text counts as an absent argument, so that the collector writes the call once, whatever the probe’s setting.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$port | an integer from 1 to 65,535 | yes | The port to reach. |
name= | a text | no (the name of host:address, or none for an IP address) | The name expected on the certificate, also announced to the server. |
authorities= | a text of PEM blocks | no (none) | Authorities added to the system’s. |
preamble= | rdp | no (none) | For Remote Desktop, which does not start with TLS: the RDP connection request goes first, then the handshake. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| What the handshake found | array('version', 'suite', 'suite_group', 'trusted', 'name_matches', 'chain', 'untrusted_reason', 'certificates') | The handshake succeeds. version: 1.0 to 1.3; suite_group: strong, acceptable, weak or tls13; trusted and name_matches: true or false; chain: complete or incomplete; untrusted_reason: '', expired, not_yet_valid, incomplete_chain, unknown_authority or insecure_signature; certificates: the chain presented, the server’s certificate first. |
| Each certificate | array('subject', 'issuer', 'names', 'not_before', 'not_after', 'days_left', 'self_issued', 'key', 'key_bits', 'signature', 'fingerprint') | In certificates. days_left is decimal, negative once the certificate has expired; key is rsa, ecdsa or ed25519; fingerprint starts with sha256:. |
| A stop, state UNREACHABLE | — | The port does not answer, the server does not speak TLS, or the handshake fails (tls_handshake_failed). |
| A stop, state MISCONFIGURED | — | An argument outside its form, never caught. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Read a site’s certificate and the days it has left.
need('host:address');
$found = tls.inspect(443);
$days = $found['certificates'][0]['days_left']; An expected name and Remote Desktop, the failure caught.
$found = tls.inspect(3389, name='web.example', preamble='rdp', errors=$err); Faults the master reports
named_argument_unknown: A name the function does not take.named_argument_invalid: A value written in clear outside its form:preamble='ssh'.argument_count:tls.inspect(), without a port.
tls.try
tls.try makes a handshake that offers only this version and, if you name one, only this suite: it says whether the server accepts them. It does not judge the certificate. A refusal from the server is an answer, not a failure: it is what the probe is looking for.
Like tls.inspect, it ignores the inherited TLS profile, and an empty text counts as an absent argument.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$port | an integer from 1 to 65,535 | yes | The port to reach. |
$version | 1.0, 1.1, 1.2 or 1.3 | yes | The only version offered. |
$suite | the name of a suite allowed in that version, or '' | no (all the suites of the version) | The only suite offered. Never with 1.3. |
name=, preamble=, errors= | as for tls.inspect | no | As for tls.inspect; there is no authorities= here. |
Returns
| What is returned | Form | When |
|---|---|---|
| The server’s answer | array('accepted' => boolean, 'version' => text, 'suite' => text, 'alert' => text) | accepted true when the handshake succeeds, with the version and suite negotiated; false when the server refuses it, with the name of the alert ('' without an alert), and version and suite empty. |
| A stop, state UNREACHABLE | — | The port does not answer. |
| A stop, state MISCONFIGURED | — | An argument outside its form, a suite with version 1.3 included. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Know whether the server still accepts TLS 1.0.
need('host:address');
$try = tls.try(443, '1.0');
if ($try['accepted']) {
fail('critical', 'TLS 1.0 accepted', 'The server accepts TLS 1.0.');
} A weak suite under TLS 1.2.
$try = tls.try(443, '1.2', 'TLS_RSA_WITH_RC4_128_SHA'); Faults the master reports
tls_version_invalid: A version written in clear outside1.0,1.1,1.2and1.3:tls.try(443, '1.4').tls_suite_invalid: A suite written in clear outside the table of suites, that the written version does not allow, or with version1.3:tls.try(443, '1.2', 'TLS_FOO'),tls.try(443, '1.3', 'TLS_RSA_WITH_RC4_128_SHA').argument_count:tls.try(443), without a version.
ntp.time
ntp.time asks the time server of protocol:ntp:server (pool.ntp.org when unset) for its time, by one NTP client request, port 123, and returns it as the server gave it at its answer, the round trip compensated as NTP does. Compared with a time read on a device, it says how far the device’s clock is off.
The request is a read and carries no access. The call never reaches the host: it asks the time server only. It requires the need('protocol:ntp') family, which brings the server and its timeout.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The server’s time | a number, in seconds since 1 January 1970 UTC, to the thousandth | The server answers with a sure time. |
| A stop, state UNREACHABLE | — | No answer from the server (timeout, host_unknown, network_error), or an answer without a sure time (ntp_answer_invalid). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
The offset of a Windows clock from the time server.
need('protocol:snmp');
need('protocol:ntp');
$date = snmp.date('1.3.6.1.2.1.25.1.2.0');
$server = ntp.time();
$offset = $date - $server; Faults the master reports
need_missing: A call withoutneed('protocol:ntp')in the header.argument_count:ntp.time(1): the call takes no positional argument.
icmp.ping
icmp.ping sends ICMP echo requests to the host’s address, host:address, IPv4 or IPv6, and counts the replies. How many leave (protocol:icmp:count, 5 by default), how long each is awaited (protocol:icmp:timeout, 2 seconds) and how far apart they leave (protocol:icmp:interval, 200 milliseconds) are settings of the tree, which the need('protocol:icmp') family brings.
No reply at all is an answer, not a failure: received at 0 and loss at 100. It is the case the probe watches. A reply that comes after its timeout counts as lost. The request asks the host for nothing but its reply.
The poller must be allowed to send ICMP. Without that right, the call stops the collector as MISCONFIGURED (icmp_not_permitted), and errors= does not catch it.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The count of the replies | array('sent' => number, 'received' => number, 'loss' => number, 'rtt_min' => number, 'rtt_avg' => number, 'rtt_max' => number, 'address' => text) | Always, replies or not: the requests sent and the replies received, the loss in percent, the response times in milliseconds ('' without a reply), and the address reached. |
| A stop, state UNREACHABLE | — | A name that does not resolve (host_unknown), or another breakdown on the poller’s side (network_error). |
| A stop, state MISCONFIGURED | — | The poller has no right to send ICMP (icmp_not_permitted, never caught). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
A host that answers no echo is down.
need('protocol:icmp');
$ping = icmp.ping();
if ($ping['received'] == 0) {
fail('critical', 'Host down', 'No reply from ' . $ping['address']);
}
value('loss', $ping['loss']); Faults the master reports
need_missing: A call withoutneed('protocol:icmp')in the header.argument_count:icmp.ping(5): the count is a setting of the tree, not an argument.
dns.resolve
dns.resolve asks a DNS server for a name and a type of record, and returns the records of that type, the code of the answer, the smallest TTL and the time the query took. The server is that of protocol:dns:server, port 53; without it, the resolver of the poller’s own system. The call never reaches the host. It requires the need('protocol:dns') family, which brings the server and its timeout.
A name that does not exist is an answer, not a failure: rcode says NXDOMAIN, and answers is empty. So is any other code of the server, SERVFAIL or REFUSED.
Each record is one text: an address for A and AAAA; a name for CNAME, NS and PTR; the preference, a space and the name for MX (10 mail.example); the strings joined for TXT; the seven fields separated by a space for SOA. A name is written in lower case, without its final dot. For PTR, the name may be an IP address, which the call writes as its reverse name.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a text | yes | The name to resolve; for PTR, an IP address or a reverse name. |
$type | A, AAAA, CNAME, MX, TXT, NS, SOA or PTR, in capitals | yes | The type of record asked for. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The answer of the server | array('answers' => list of texts, 'rcode' => text, 'ttl' => number, 'ms' => number) | The server answers, whatever its code: the records of the type asked, in the server’s order; the code of the answer (NOERROR, NXDOMAIN, SERVFAIL…); the smallest TTL, in seconds, '' without a record; the time of the query, in milliseconds. |
| A stop, state UNREACHABLE | — | The server does not answer (timeout), refuses the query (connection_refused), or cannot be reached (host_unknown, network_error). |
| A stop, state MISCONFIGURED | — | A computed type outside the eight. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Whether a name resolves.
need('protocol:dns');
$found = dns.resolve('www.example', 'A');
$resolved = 'no';
if ($found['rcode'] == 'NOERROR' && list.count($found['answers']) > 0) {
$resolved = 'yes';
} The mail servers of a domain.
$mail = dns.resolve('example', 'MX'); Faults the master reports
need_missing: A call withoutneed('protocol:dns')in the header.dns_type_invalid: A type written in clear outside the eight, or in lower case:dns.resolve('www.example', 'ANY'),dns.resolve('www.example', 'a').argument_count:dns.resolve('www.example'), without a type.
tcp.connect
tcp.connect opens a TCP connection to the host’s address, host:address, on a port, and closes it at once, sending nothing: the service behind the port receives no request. It says whether the port accepts a connection, and how long the opening took.
A closed or filtered port is an answer, not a failure: open is false when the host refuses the connection, when nothing answers within protocol:tcp:timeout (5 seconds by default), or when a router says the host cannot be reached. The call requires the need('protocol:tcp') family.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$port | an integer from 1 to 65,535 | yes | The port to open. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The state of the port | array('open' => boolean, 'ms' => number) | Always, open or not: ms is the time of the opening, in milliseconds, '' when the port is closed. |
| A stop, state UNREACHABLE | — | A name that does not resolve (host_unknown), or another breakdown on the poller’s side (network_error). |
| A stop, state MISCONFIGURED | — | A port outside 1 to 65,535. |
'' | empty text | The call failed and errors= caught the failure. |
Examples
Whether SSH listens.
need('protocol:tcp');
$ssh = tcp.connect(22);
if (!$ssh['open']) {
fail('critical', 'SSH closed', 'Port 22 refuses the connection.');
} Faults the master reports
need_missing: A call withoutneed('protocol:tcp')in the header.argument_count:tcp.connect(), without a port.
The cloud providers
One function per provider, which signs a request as the provider requires or gets its token: aws, ovh, google. Each reads the keys of its family, declared with need. A secret key never leaves: only the signature leaves, or the token obtained with it.
A signed request leaves through http.*, with the method, the address and the body the signature received, written identically: one byte of difference makes the provider refuse the signature, an error answer the collector reads. The time of the signature is the poller’s, at the moment of the call: a wrong clock makes the request refused.
aws.sign
aws.sign signs a request for AWS, with Signature Version 4 (AWS4-HMAC-SHA256), with the keys of protocol:aws. It sends nothing: it returns the headers to pass to headers= of the http.* call that sends the request.
It returns the headers of $headers, then Authorization and X-Amz-Date; X-Amz-Security-Token when protocol:aws:session-token has a value; X-Amz-Content-Sha256 for the s3 service, which requires it. Authorization and X-Amz-Security-Token are secrets: they go only into a protocol call or into the cache, and an http:// call that carries them stops the collector.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$method | GET, POST, PUT or DELETE, in capitals | yes | The method of the request. |
$url | a whole https:// or http:// address | yes | The address of the request, its query included. |
$headers | an array(name => value), the form of headers= | yes | The headers the request carries, array() for none. Neither Authorization nor an X-Amz-… header the function writes. |
$body | a text | yes | The body of the request, '' without a body. |
$region | a non-empty text, without / | yes | The region: us-east-1. |
$service | a non-empty text, without / | yes | The service: ec2, monitoring, s3. |
Returns
| What is returned | Form | When |
|---|---|---|
| The headers of the request | array(name => value) | Always: those of $headers, and those the function writes. |
| A stop, state MISCONFIGURED | — | A method outside the four, an address that is not one, headers outside their form, an empty region or service. |
Examples
A signed request to CloudWatch, in the probe’s region.
need('protocol:http');
need('protocol:aws');
$region = variable('aws:region', 'text', array('label' => 'Region', 'help' => 'The AWS region, us-east-1 for instance.'));
$url = 'https://monitoring.' . $region . '.amazonaws.com/?Action=ListMetrics&Version=2010-08-01';
$answer = http.get($url, headers=aws.sign('GET', $url, array('Accept' => 'application/json'), '', $region, 'monitoring')); Faults the master reports
need_missing: A call withoutneed('protocol:aws')in the header.argument_count: A number of arguments other than six.named_argument_unknown: A named argument:aws.signtakes none, not evenerrors=.
ovh.sign
ovh.sign signs a request for the OVHcloud API, with the keys of protocol:ovh. It sends nothing: it returns the X-Ovh-* headers to pass to headers=.
The signature is $1$ followed by the SHA-1 digest of the application secret, the consumer key, the method, the address, the body and the timestamp, joined by +: the one OVHcloud imposes. X-Ovh-Consumer and X-Ovh-Signature are secrets.
The address of the API, protocol:ovh:endpoint, is read with its own need: the keys of an application are valid only on the API where they were created.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$method | GET, POST, PUT or DELETE, in capitals | yes | The method of the request. |
$url | a whole https:// or http:// address | yes | The address of the request, as the http.* call will write it, its query included. |
$body | a text | yes | The body of the request, '' without a body. |
Returns
| What is returned | Form | When |
|---|---|---|
| The headers of the signature | array('X-Ovh-Application' => …, 'X-Ovh-Consumer' => …, 'X-Ovh-Timestamp' => …, 'X-Ovh-Signature' => …) | Always. |
| A stop, state MISCONFIGURED | — | A method outside the four, or an address that is not one. |
Examples
The account’s bills.
need('protocol:http');
need('protocol:ovh');
$api = need('protocol:ovh:endpoint');
$url = $api . '/me/bill';
$answer = http.get($url, headers=ovh.sign('GET', $url, '')); Faults the master reports
need_missing: A call withoutneed('protocol:ovh')in the header.argument_count: A number of arguments other than three.named_argument_unknown: A named argument:ovh.signtakes none.
google.token
google.token asks Google’s token server for an access token, for the service account whose key protocol:google:service-account-key carries: an assertion signed with the private key, exchanged for a token. It is the only request it sends, always to that server, whose certificate is judged by the “Standard” profile: the probe’s TLS profile applies to its target, not to Google.
The token is valid for an hour. Keep it in the cache for less, and read it back from there. It is a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$scope | a non-empty text | yes | One or more scopes, separated by a space. |
errors= | a lone variable | no (the failure stops the collector) | Catches the failure of the call: see errors=. |
Returns
| What is returned | Form | When |
|---|---|---|
| The access token | text, secret | Google accepted the key and the scope. |
| A stop, state MISCONFIGURED | — | Google refuses the key or the scope (access_denied); an unreadable key or an empty scope, which errors= does not catch. |
| A stop, state UNREACHABLE | — | The server does not answer within 10 seconds (timeout), or answers something other than a token (network_error). |
'' | empty text | The call failed and errors= caught the failure. |
Examples
A token kept in the cache, then a request to a Google API.
need('protocol:http');
need('protocol:google');
if (cache.has('token')) {
$token = cache.get('token');
} else {
$token = google.token('https://www.googleapis.com/auth/monitoring.read');
cache.set('token', $token, 3000);
}
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . $token)); Faults the master reports
need_missing: A call withoutneed('protocol:google')in the header.argument_count:google.token(), without a scope.named_argument_unknown: A named argument other thanerrors=.
The tools
Processing functions, with no output to the outside: they work on values, on time or on the probe’s cache. In order: json, xml, x509, text, number, list, counter, time, cache, secret, then the encodings url, base64, hex and hash.
json.read
json.read turns a JSON text into a value. Objects and lists become arrays.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The JSON text to read. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value the text writes | array, text, number or boolean | The text is JSON. |
| A stop, state UNREACHABLE | — | The text is not JSON. |
Examples
Read the declared state in the body of a response.
$health = list.get(json.read($answer['body']), 'status', 'absent'); Read a list of jobs.
$list = json.read($answer['body']);
foreach ($list['jobs'] as $job) {
value('result', $job['last_result'], $job['name']);
} json.write
json.write turns a value into a JSON text, for example for the body of a request.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$value | text, number, boolean or array | yes | The value to write. |
Returns
| What is returned | Form | When |
|---|---|---|
| The JSON text of the value | text | Always. |
Examples
Write the body of a login request.
$body = json.write(array('username' => $user, 'password' => $password)); xml.read
xml.read turns an XML text into a value. Each element is put in a list under its name, even alone, the root included: xml.read('<status code="200">ok</status>') is array('status' => array(array('@code' => '200', '#text' => 'ok'))), read as $r['status'][0]['@code']. A foreach over elements therefore counts right, whether there is one or several.
An element without attributes or children is its text: <name>web1</name> is 'web1', <name/> is '', which list.is tells from an array. The others are arrays: each attribute under @ followed by its name, the text under #text when it is not empty, and under the name of each child the list of the children of that name. The text loses its leading and trailing blanks, and every value read is a text.
A name is written as the document writes it, prefix included (atom:entry): no namespace is resolved, and xmlns is an attribute like any other. The <?xml …?> declaration, the comments and the DTD do not count. It is a reading, with no file and no network: no entity is loaded, and only the five entities of XML (<, >, &, ', ") and character references are read.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The XML text to read. |
Returns
| What is returned | Form | When |
|---|---|---|
| The root, in a list under its name | array | The text is well-formed XML. |
| A stop, state UNREACHABLE | — | The text is not well-formed XML, or it carries an entity other than the five of XML. |
Examples
Read the token of a session.
$logon = xml.read($answer['body']);
$session = $logon['LogonResponse'][0]['X-API-Session'][0]; Read the entries of a feed, one or several, or none.
$feed = xml.read($answer['body']);
foreach (list.get($feed['feed'][0], 'entry', array()) as $entry) {
value('state', $entry['content'][0]['State'][0], $entry['id'][0]);
} xml.escape
xml.escape writes a text so that it fits in an XML body: &, <, >, " and ' become &, <, >, " and ', and the tab and line breaks become character references. The text then fits as it is in the text of an element as in the value of an attribute.
Pass through it every text you place in an XML body: glued by concatenation alone, an & or a < in a password would break the body, with no fault. xml.escape of a secret stays a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to write. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text written for XML | text | The text carries only characters XML accepts. |
| A stop, state MISCONFIGURED | — | The text carries a control character other than the tab and line breaks, which XML does not accept. |
Examples
Write the body of a session opening.
$body = '<LogonRequest><UserID>' . xml.escape($user) . '</UserID><Password>' . xml.escape($password) . '</Password></LogonRequest>';
$answer = http.put($base . '/rest/api/web/Logon', $body, headers=array('Content-Type' => 'application/xml')); Faults the master reports
secret_written: A secret visible on reading passed tovaluethroughxml.escape.
x509.crl
x509.crl reads an X.509 revocation list, usually fetched by http.get from its distribution point, and returns what it says of itself: its date of publication, the date by which the next one is promised, its issuer and the number of certificates it revokes. A list whose next date has passed makes clients refuse every certificate of the authority: that is what is monitored.
The text is the list in PEM (-----BEGIN X509 CRL-----) or in DER. The signature of the list is not checked. It is a reading, with no file and no network.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$der_or_pem | a text | yes | The list, in PEM or in DER. |
Returns
| What is returned | Form | When |
|---|---|---|
| What the list says | array('this_update' => number, 'next_update' => number, 'issuer' => text, 'entries' => number) | The text is a revocation list. The dates in seconds since 1 January 1970 UTC; next_update is '' when the list does not give it. |
| A stop, state UNREACHABLE | — | The text is not a revocation list: a certificate, an error page, a truncated list. |
Examples
The number of days before the list of an authority expires.
need('protocol:http');
$answer = http.get('http://pki.example/CertEnroll/root.crl');
$crl = x509.crl($answer['body']);
value('days-left', number.round(($crl['next_update'] - time.now()) / 86400, 1)); Faults the master reports
argument_count:x509.crl(), without a list.
text.trim
text.trim removes the blanks at the start and at the end of a text.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to clean. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text without its leading and trailing blanks | text | Always. |
Examples
Clean a command’s output.
$name = text.trim($host['output']); text.lower
text.lower turns a text into lowercase, to compare without worrying about case.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to convert. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text in lowercase | text | Always. |
Examples
Compare a state regardless of case.
if (text.lower($state) == 'up') {
$up = $up + 1;
} text.length
text.length counts the characters of a text, not its bytes: text.length('é') is 1.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to measure. |
Returns
| What is returned | Form | When |
|---|---|---|
| The number of characters | number | Always. |
Examples
Count the characters of a name.
$size = text.length($name); text.contains
text.contains says whether a text contains a given piece.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to search in. |
$part | a text | yes | The piece looked for. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the piece is in the text. |
Examples
Look for a word in a page.
if ($word != '' && !text.contains($answer['body'], $word)) {
fail('critical', 'Word missing', 'The page does not contain the word ' . $word . '.');
} text.split
text.split cuts a text at each separator and returns the list of pieces.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to cut. |
$separator | a text | yes | What separates two pieces. |
Returns
| What is returned | Form | When |
|---|---|---|
| The list of pieces | array | Always. |
Examples
Split the values of a list written with commas.
$parts = text.split('a,b,c', ','); text.join
text.join joins the elements of an array into a text, with a separator.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | an array | yes | The elements to join. |
$separator | a text | yes | What goes between two elements. |
Returns
| What is returned | Form | When |
|---|---|---|
| The elements joined by the separator | text | Always. |
Examples
Join a list of files in a command.
$sys = ssh.run('cd /sys/class/net && grep -s -H . */' . text.join($files, ' */')); text.words
text.words cuts a text into words, separated by blanks.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to cut. |
Returns
| What is returned | Form | When |
|---|---|---|
| The list of words | array | Always. |
Examples
Cut a line of /proc/stat into columns.
$f = text.words($line);
$idle = $f[4] + $f[5]; text.lines
text.lines cuts a text into lines, leaving out the empty ones.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to cut. |
Returns
| What is returned | Form | When |
|---|---|---|
| The list of non-empty lines | array | Always. Each line is returned without its leading and trailing blanks. |
Examples
Walk through a command’s output line by line.
foreach (text.lines($units['output']) as $line) {
$m = text.match($line, '([^ ]+)\.service +[^ ]+ +([^ ]+)');
} text.match
text.match looks for a pattern in a text and returns the parts that match. The pattern is a regular expression, in Go’s syntax (RE2), which runs in linear time: it cannot run away.
The parts are numbered: 0 for all that matches, then 1, 2… for each group in parentheses. Written in clear, the pattern is judged as soon as the collector is saved.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to search in. |
$pattern | a text | yes | The pattern, a Go regular expression. |
Returns
| What is returned | Form | When |
|---|---|---|
| The groups of the first match | array | The pattern matches. Element 0 is all that matches, 1, 2… are the groups. |
array() | empty array | Nothing matches. |
Examples
Split the name of an interface, a file and its value in a line eth0/mtu:1500.
$m = text.match($line, '^([^/]+)/([^:]+):(.*)$');
$read[$m[1]][$m[2]] = $m[3]; Faults the master reports
pattern_invalid: A pattern written in clear that is not a Go regular expression.
text.is_number
text.is_number says whether a text is the writing of a number: an optional -, digits, and optionally a dot followed by digits.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to examine. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the text is the writing of a number (200, -3, 0.52). |
Examples
Calculate only on what is a number.
if (text.is_number($f[2])) {
$used = $f[2] * 1024;
} text.like
text.like compares a whole text with a simple pattern, the kind an administrator types without a regular expression. It is also the pattern of a selection (per).
In the pattern, * stands for any run of characters, empty included, and ? for one character. Any other character stands for itself, upper and lower case being different.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The text to compare. |
$pattern | a text | yes | The pattern, with * and ?. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the whole text follows the pattern. |
Examples
text.like('docker0', 'docker*') is true.
if (text.like($name, 'docker*')) {
continue;
} number.round
number.round rounds a number to the integer, or to a given number of decimals.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$n | a number | yes | The number to round. |
$decimals | an integer | no (rounded to the integer) | The number of decimals to keep. |
Returns
| What is returned | Form | When |
|---|---|---|
| The rounded number | number | Always. |
Examples
Round a rate to the integer.
value('traffic-in', number.round($in), $name); Keep two decimals.
value('packets', number.round($packets, 2), $name); list.count
list.count counts the elements of an array.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | an array | yes | The array to count. |
Returns
| What is returned | Form | When |
|---|---|---|
| The number of elements | number | Always. |
Examples
Count the members of a bond.
value('bond-members', list.count($members), $name); list.has
list.has says whether a key exists in an array.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | an array | yes | The array to examine. |
$key | a text or an integer | yes | The key looked for. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the key exists. |
Examples
Test a key before using it.
if (list.has($f, 'duplex')) {
value('duplex', $f['duplex'], $name);
} list.get
list.get reads a cell of an array when the key may be missing, and returns a replacement value if it is. Reading an absent key directly stops the collector; list.get avoids that.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | an array | yes | The array to read. |
$key | a text or an integer | yes | The key looked for. |
$default | a value | yes | What the call returns when the key does not exist. |
Returns
| What is returned | Form | When |
|---|---|---|
| The element under the key | the value of the cell | The key exists. |
$default | the value given | The key does not exist. |
Examples
Read a cell that is missing for a down interface.
$speed = list.get($f, 'speed', 0); Count with a starting value.
$address_count[$words[1]] = list.get($address_count, $words[1], 0) + 1; list.add
list.add returns the array with a value added. The value is filed under the key that follows the array’s largest integer key (0 if it has none), as $a[] = $x did in PHP.
An array is a value: list.add does not change the array it receives, it returns a new one. Store the result.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | an array | yes | The array to complete. |
$value | a value | yes | What to add. |
Returns
| What is returned | Form | When |
|---|---|---|
| The array with the value added | array | Always. |
Examples
Add an element to a list.
$names = array();
$names = list.add($names, 'eth0');
$names = list.add($names, 'eth1'); list.is
list.is says whether a value is an array. It tells an empty XML element, <name/>, read as the text '', from an element that has children, read as an array; or a JSON null, read as '', from an object.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$value | a value | yes | The value to examine. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the value is an array; false for a text, a number or a boolean. |
Examples
Count the items of a set that may come back empty, <itemSet/>.
$answer = xml.read($r['body']);
$set = $answer['DescribeResponse'][0]['itemSet'][0];
$count = 0;
if (list.is($set)) {
$count = list.count(list.get($set, 'item', array()));
}
value('items', $count); counter.rate
counter.rate computes the per-second rate of a counter: the difference with the previous run’s reading, divided by the time elapsed between the two. It is the usual case of an interface, a disk or a processor, whose counter only grows.
The function keeps this run’s reading (the value and the time) for the next one. Its readings are kept by the poller with the probe’s cache, apart from its 32 keys, one per name and per instance.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$name | a text written in clear, in the form of a cache key | yes | The name of the counter. |
$counter | a number, or the writing of a number | yes | The counter’s value at this run. |
$instance | a text | no (none) | The instance the reading is about, to keep one reading per instance. |
Returns
| What is returned | Form | When |
|---|---|---|
| The per-second rate | number | There is a previous reading, not older than three times the probe’s interval, and the counter has not gone backwards. |
'' | empty text | At the first reading; when the previous reading is too old; when the counter has gone backwards (a device restarted, a counter that overflows). The new reading serves for the next run. |
| A stop, state MISCONFIGURED | — | The function is called twice for the same name and the same instance in a run; or a secret is passed to it. |
| A stop, state UNREACHABLE | — | $counter is a text that is not the writing of a number. |
Examples
The incoming rate of an interface in bits per second, nothing at the first run.
$in = counter.rate('in', $f['statistics/rx_bytes'] * 8, $name); Without an instance, for a single counter.
$in = counter.rate('in', $current['in'] * 8);
if ($in == '') {
skip('First reading: the rate will come at the next run');
} Faults the master reports
counter_name_invalid: A counter name outside the form of a cache key.argument_not_literal: A name that is not written in clear.secret_written: A secret visible on reading, passed tocounter.rate.
time.now
time.now returns the time, in seconds, to the thousandth.
Parameters
time.now takes no parameter.
Returns
| What is returned | Form | When |
|---|---|---|
| The time | number, in seconds, to the thousandth | Always. |
Examples
Keep the time of a reading in the cache.
cache.set('last-run', time.now(), 3600); time.parse
time.parse reads a date written as a text, and returns the instant it says, in seconds since 1 January 1970 UTC, to the thousandth: the form of time.now, so that the two subtract.
Three formats exist, named in lower case: rfc3339 (2026-10-27T13:30:15Z, 2026-10-27T15:30:15.25+02:00), the form of APIs; iso8601, the same, which also accepts the offset +0200 or +02 and the fraction after a comma; rfc1123 (Tue, 27 Oct 2026 13:30:15 GMT), the date of HTTP and of mail, carried by the date and last-modified headers.
The offset to UTC is always required: without it, the instant depends on a time zone the text does not give, and guessing it would make it wrong by the whole zone. For the local time of a server, add the offset you know, for instance that of date +%z. A date of another form is cut by text.match and rewritten in one of the three formats.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text | yes | The date to read, whole, without leading or trailing blanks. |
$format | rfc3339, iso8601 or rfc1123 | yes | The format the date is written in. |
Returns
| What is returned | Form | When |
|---|---|---|
| The instant | number, in seconds since 1 January 1970 UTC, to the thousandth | The text follows its format, each field within the bounds of the calendar. |
| A stop, state UNREACHABLE | — | The text does not follow its format, has no offset, or carries a date that does not exist (a 30 February). |
| A stop, state MISCONFIGURED | — | A computed format outside the three. |
Examples
The age, in hours, of a backup dated by an API.
$age = (time.now() - time.parse($job['lastRun'], 'rfc3339')) / 3600; A local time, to which the offset of the server is added.
$zone = ssh.run('date +%z');
$when = time.parse($archive['time'] . text.trim($zone['output']), 'iso8601'); Faults the master reports
time_format_invalid: A format written in clear outside the three, or in capitals:time.parse($t, 'RFC3339'),time.parse($t, 'yyyy-mm-dd').argument_count:time.parse($t), without a format.
time.format
time.format writes an instant, in seconds since 1 January 1970 UTC (the form of time.now), in one of the three formats of time.parse, always in UTC: the reverse of time.parse, to write a date in the address or the body of a request.
rfc3339 and iso8601 write 2026-10-27T13:30:15Z, with the fraction to the thousandth when the instant is not a round second (2026-10-27T13:30:15.25Z); rfc1123 writes the date of HTTP, Tue, 27 Oct 2026 13:30:15 GMT, without a fraction. For a round second, pass number.round($seconds). Another form is drawn from this one by text.split.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$seconds | a number | yes | The instant, in seconds since 1 January 1970 UTC. |
$format | rfc3339, iso8601 or rfc1123 | yes | The format to write it in. |
Returns
| What is returned | Form | When |
|---|---|---|
| The date | text, in UTC | The instant is a number, between the years 0000 and 9999. |
| A stop, state UNREACHABLE | — | The instant is not a number, or falls outside the years 0000 to 9999. |
| A stop, state MISCONFIGURED | — | A computed format outside the three. |
Examples
The start of a thirty-minute window, for an API that asks for it in UTC.
$start = time.format(number.round(time.now() - 1800), 'rfc3339');
$query = url.query(array('timespan' => $start . '/PT30M')); Today’s date, without the time.
$parts = text.split(time.format(time.now(), 'rfc3339'), 'T');
$day = $parts[0]; Faults the master reports
time_format_invalid: A format written in clear outside the three, or in capitals:time.format($t, 'RFC1123').argument_count:time.format($t), without a format.
time.wait
time.wait waits a number of seconds before going on, within the time the collector is allowed. Beyond that time, the collector stops.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$seconds | a number | yes | The time to wait, in seconds. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | time.wait returns no value. |
| A stop, state UNREACHABLE | — | The time allowed to the run is exceeded. |
Examples
Wait two seconds.
time.wait(2); cache.set
The cache is a small memory that the poller keeps for each probe, from one run to the next. It serves to keep a token or any reading the collector wants to find again. Each key has a lifetime: a value that is not set again before its end disappears.
cache.set sets a key for a duration, replacing its earlier value and duration. The write counts from the call, however the run ends. The cache survives a restart of the poller and is erased when the probe is deleted or changes collector or version. The master never reads it.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$key | a text: 1 to 64 characters among a-z, 0-9, ., _, - | yes | The key. 32 keys at most per probe. |
$value | text, number or array, 16,384 characters at most written as json.write would write it | yes | The value to keep. A secret set in the cache is encrypted there and comes back secret. |
$seconds | an integer from 1 to 604,800 (seven days) | yes | The lifetime. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | cache.set returns no value. |
| A stop, state MISCONFIGURED | — | A limit of the cache is exceeded. |
Examples
Keep a token for fifty minutes.
cache.set('token', $session['access_token'], 3000); Faults the master reports
cache_key_invalid: A key written in clear outside its form.cache_lifetime_invalid: A lifetime written in clear outside its limits.
cache.get
cache.get returns the value kept under a key. An absent or expired key stops the collector: test it first with cache.has.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$key | a text | yes | The key to read. |
Returns
| What is returned | Form | When |
|---|---|---|
| The value kept | text, number or array | The key exists and has not expired. |
| A stop of the collector | — | The key is absent or expired. |
Examples
Read a token that was kept, after checking that it exists.
if (cache.has('token')) {
$token = cache.get('token');
} Faults the master reports
cache_key_invalid: A key written in clear outside its form.
cache.has
cache.has says whether a key is in the cache. An expired key is no longer there.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$key | a text | yes | The key to look for. |
Returns
| What is returned | Form | When |
|---|---|---|
true or false | boolean | true if the key is there. |
Examples
Log in only when no token is kept.
if (cache.has('token')) {
$token = cache.get('token');
} else {
$token = login($base, $user, $password);
} Faults the master reports
cache_key_invalid: A key written in clear outside its form.
cache.delete
cache.delete removes a key from the cache. Removing a key that is not there is not a fault.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$key | a text | yes | The key to remove. |
Returns
| What is returned | Form | When |
|---|---|---|
| Nothing | — | cache.delete returns no value. |
Examples
Forget a token that was refused.
if ($answer['status'] == 401) {
cache.delete('token');
} Faults the master reports
cache_key_invalid: A key written in clear outside its form.
secret.mark
secret.mark marks a value as secret. Secrets are: the value of a secret variable, a value passed through secret.mark, a secret value read back from the cache, everything computed from a secret ('Bearer ' . $token is, as is an encoding, a hash or a signature of a secret), and the response to a request whose body carried a secret, except its code and its time, unless the call declares it free of tokens (public=true, see http.post). The result of a comparison is not.
Any part of a response that holds a secret of the probe is secret too, whatever the call and whatever the request carried, public=true included: a token a target copies into its response never reaches a value. The secrets of the probe are the non-empty secret values the collector holds at the time of the call: its secret variables, and whatever it marked, read back from the cache or drew from a secret, such as the token of a login. The SNMP v2c community is not searched: it travels in the clear by nature, and public would make the interface alias public-lan secret. Each is searched as it is, in base64, in address base64 and passed through url.encode, and the account of a user= in base64 as user:password. What becomes secret is the element of the response that holds it: the body or the headers of an HTTP response, the output of a command, a value of an snmp.walk. A secret taken up otherwise, in hexadecimal or in part, is not seen.
A secret goes only into a protocol function (a header, a body, an SSH argument) or into the cache. Passed to value, fail, skip, watch.raise or counter.rate, it stops the collector. No secret goes to a target whose identity is not judged: an http:// request that carries one stops the collector.
This is a guard against accidents, not against an author set on cheating: against them, the safeguard remains the reading of the diff on the poller.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$value | a value | yes | The value to mark. |
Returns
| What is returned | Form | When |
|---|---|---|
| The same value, marked secret | that of $value | Always. |
| A stop, state MISCONFIGURED | — | A secret is written into a value, a message or a counter, or sent over http://. |
Examples
Mark a token read elsewhere before putting it in a header.
$token = secret.mark($raw_token);
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . $token)); Faults the master reports
secret_written: A secret visible on reading, passed tovalue,fail,skip,watch.raiseorcounter.rate.
url.encode
url.encode writes a text so that it fits in an address or in a form body (RFC 3986). ASCII letters, digits and -, ., _, ~ stay as they are; every other byte is written % and two uppercase hexadecimal digits, the space included (%20).
An address parameter is written with url.encode or url.query, never by concatenation alone: a text that carries &, =, + or a space would break the address without a fault. The encoding of a secret is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to write. |
Returns
| What is returned | Form | When |
|---|---|---|
| The encoded text | text | Always. url.encode('a b&c=d') is a%20b%26c%3Dd. |
Examples
Pass a PromQL query in the address.
need('protocol:http');
$answer = http.get('https://prometheus.example/api/v1/query?query=' . url.encode('up{job="node"}')); Faults the master reports
argument_count:url.encode(), with no text.
url.query
url.query writes an array as parameters: each element becomes key=value, the key and the value passed through url.encode, joined by &. This is the form of a form body and of the end of an address.
The parameters are always sorted by encoded key, byte by byte, with no option: a signed request requires it, and a server that reads a form reads them by name. Two parameters of the same name (a=1&a=2) are not written with url.query. An array that carries a secret gives a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$array | array(key => value), each value a text or a number | yes | The parameters to write. |
Returns
| What is returned | Form | When |
|---|---|---|
| The joined parameters | text | Always; '' for an empty array. |
| A stop, state MISCONFIGURED | — | The argument is not an array, or a value is neither a text nor a number. |
Examples
Ask for an OAuth2 token through the client credentials flow, the body written as a form.
need('protocol:http');
$id = variable('oauth:client-id', 'text', array('label' => 'Client ID', 'help' => 'The application’s identifier.'));
$secret = variable('oauth:client-secret', 'text', array('label' => 'Client secret', 'help' => 'The application’s secret.', 'secret' => true));
$body = url.query(array('grant_type' => 'client_credentials', 'client_id' => $id, 'client_secret' => $secret, 'scope' => 'https://graph.example/.default'));
$answer = http.post('https://login.example/oauth2/token', $body, headers=array('Content-Type' => 'application/x-www-form-urlencoded')); Faults the master reports
argument_count:url.query(array('a' => 1), '&'): a single argument.secret_written: A secret written in the array passed tovalue:value('x', url.query(array('k' => $password))).
base64.encode
base64.encode writes the UTF-8 bytes of a text in base64, with the standard alphabet and its = padding (RFC 4648, section 4). The encoding of a secret is still a secret: it is read back in one line.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to encode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text in base64 | text | Always. base64.encode('banshee') is YmFuc2hlZQ==. |
Examples
Write the Authorization: Basic header yourself, here the one an OAuth2 server asks of a client (RFC 6749, section 2.3.1): user= refuses an account that contains :.
need('protocol:http');
$id = variable('oauth:client-id', 'text', array('label' => 'Client ID', 'help' => 'The application’s identifier.'));
$secret = variable('oauth:client-secret', 'text', array('label' => 'Client secret', 'help' => 'The application’s secret.', 'secret' => true));
$basic = base64.encode(url.encode($id) . ':' . url.encode($secret));
$answer = http.post('https://login.example/oauth2/token', url.query(array('grant_type' => 'client_credentials')), headers=array('Authorization' => 'Basic ' . $basic, 'Content-Type' => 'application/x-www-form-urlencoded')); Faults the master reports
argument_count:base64.encode('a', 'b'): a single argument.secret_written: The encoding of a secret passed tovalue:value('x', base64.encode($password)).
base64.decode
base64.decode returns the text that a standard base64 writes. Line breaks in it are ignored. The decoding of a secret is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text in standard base64, padding included | yes | The text to decode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The decoded text | text | The text is base64. base64.decode('YmFuc2hlZQ==') is banshee. |
| A stop, state UNREACHABLE | — | The text is not base64: a character outside the alphabet, a missing or wrong padding. |
Examples
Read a value that Consul returns in base64.
need('protocol:http');
$answer = http.get('https://consul.example:8501/v1/kv/app/mode');
$entries = json.read($answer['body']);
$mode = base64.decode($entries[0]['Value']); Faults the master reports
argument_count:base64.decode(), with no text.
base64.url_encode
base64.url_encode writes the UTF-8 bytes of a text in base64 for an address: - and _ replace + and /, with no padding (RFC 4648, section 5). This is the form of the parts of a JWT. The encoding of a secret is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to encode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text in base64 for an address | text | Always. base64.url_encode('???') is Pz8_, base64.url_encode('banshee') is YmFuc2hlZQ. |
Examples
Encode a text for an address parameter.
$e = base64.url_encode('banshee'); Faults the master reports
argument_count:base64.url_encode(), with no text.secret_written: The encoding of a secret passed tovalue:value('x', 'h=' . base64.url_encode($password)).
base64.url_decode
base64.url_decode returns the text that a base64 for an address writes, with or without its = padding; a padding that is present must be complete. Line breaks in it are ignored. The decoding of a secret is still a secret: the payload of a secret token is one too, and is only compared with another value.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text in base64 for an address | yes | The text to decode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The decoded text | text | The text is base64 for an address. base64.url_decode('Pz8_') is ???. |
| A stop, state UNREACHABLE | — | The text is not of this form: a + or a /, an incomplete padding. |
Examples
Read the payload of a JWT, its second part, and renew it five minutes before it expires.
$parts = text.split($jwt, '.');
$claims = json.read(base64.url_decode($parts[1]));
if ($claims['exp'] < time.now() + 300) {
$renew = true;
} Faults the master reports
argument_count:base64.url_decode('a', 'b'): a single argument.
hex.encode
hex.encode writes the UTF-8 bytes of a text in lowercase hexadecimal, two digits per byte. The encoding of a secret is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to encode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The text in hexadecimal | text | Always. hex.encode('banshee') is 62616e73686565. |
Examples
Encode a text, then read it back.
$e = hex.encode('banshee');
$d = hex.decode($e); Faults the master reports
argument_count:hex.encode(), with no text.
hex.decode
hex.decode returns the text that a hexadecimal writes, its digits in lowercase or uppercase. The decoding of a secret is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | an even number of digits 0-9, a-f, A-F | yes | The text to decode. |
Returns
| What is returned | Form | When |
|---|---|---|
| The decoded text | text | The text is hexadecimal. hex.decode('62616E73686565') is banshee. |
| A stop, state UNREACHABLE | — | An odd number of digits, or a character outside 0-9, a-f, A-F. |
Examples
Read back a text written in hexadecimal.
$d = hex.decode('62616E73686565'); Faults the master reports
argument_count:hex.decode('61', '62'): a single argument.
hash.sha256
hash.sha256 returns the SHA-256 hash of the UTF-8 bytes of a text. Only safe hashes exist: neither MD5 nor SHA-1.
The hash of a secret is still a secret: that of a short secret is found by trial. It can be compared with another value.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to hash. |
Returns
| What is returned | Form | When |
|---|---|---|
| The hash | text, 64 lowercase hexadecimal digits | Always. hash.sha256('') is e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. |
Examples
Know whether a page has changed, by comparing its hash with the expected one.
need('protocol:http');
$answer = http.get('https://www.example/');
if (hash.sha256($answer['body']) != $known) {
fail('warning', 'Page changed', 'The content of the page has changed.');
} Faults the master reports
function_unknown:hash.md5('a')andhash.sha1('a').argument_count:hash.sha256(), with no text.secret_written: The hash of a secret passed tovalue:value('x', hash.sha256($password)).
hash.hmac_sha256
hash.hmac_sha256 returns the HMAC-SHA-256 signature (RFC 2104) of a text under a key: the text first, the key second.
The key is usually a secret, which comes from a secret variable and is never written in clear in the code. The signature of a secret text, or under a secret key, is still a secret.
Parameters
| Name | Form | Required (default) | Role |
|---|---|---|---|
$text | a text, or a number, which stands for its writing | yes | The text to sign. |
$key | a text | yes | The key of the signature. |
Returns
| What is returned | Form | When |
|---|---|---|
| The signature | text, 64 lowercase hexadecimal digits | Always. hash.hmac_sha256('what do ya want for nothing?', 'Jefe') is 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843 (RFC 4231, case 2). |
Examples
Sign the time of a request under a shared key, and pass the time and the signature as parameters.
need('protocol:http');
$key = variable('api:signing-key', 'text', array('label' => 'Signing key', 'help' => 'The key shared with the API.', 'secret' => true));
$stamp = number.round(time.now());
$signature = hash.hmac_sha256($stamp, $key);
$answer = http.get('https://api.example/status?' . url.query(array('ts' => $stamp, 'signature' => $signature))); Faults the master reports
argument_count:hash.hmac_sha256('a'), with no key.secret_written: The signature under a secret key passed tovalue:value('x', hash.hmac_sha256('t', $key)).