-
Notifications
You must be signed in to change notification settings - Fork 165
Hooks
Hooks provide a way to be notified of certain events during the operation of the Razor server; the behavior of a hook is defined by a hook type.
Similar to brokers and tasks, hook types are defined through a .hook
directory and files within that directory:
hooks/
some.hook/
configuration.yaml
node-bind-policy
node-unbind-policy
...
The hook type specifies the configuration data that it accepts in
configuration.yaml
; that file must define a hash:
foo:
description: "Explain what foo is for"
default: 0
bar:
description "Explain what bar is for"
default: "Barbara"
...
For each event that the hook type handles, it must contain a script with the event's name; that script must be executable by the Razor server. All hook scripts for a certain event are run (in an indeterminate order) when that event occurs.
The create-hook
command is used to create a hook from a hook type:
> razor create-hook --name myhook --hook-type some_hook \
--configuration foo=7 --configuration bar=rhubarb
Similarly, the delete-hook
command is used to remove a hook.
The general protocol is that hook event scripts receive a JSON object on their stdin, and may return a result by printing a JSON object to their stdout. The properties of the input object vary by event, but they always contain a 'hook' property:
{
"hook": {
"name": hook name,
"configuration": ... user-defined object ...
}
}
The configuration
object is initialized from the Hash described in
the hook's configuration.yaml
and the properties set by the
create-hook
command. With the create-hook
command above, this
would result in:
{
"hook": {
"name": "myhook",
"configuration": {
"foo": 7,
"bar": "rhubarb"
}
}
}
The script may return data by producing a JSON object on its stdout to
indicate changes that should be made to the hook's configuration
; the
updated configuration
will be used on subsequent invocations of any
event for that hook. The output must indicate which properties to
update, and which ones to remove:
{
"hook": {
"configuration": {
"update": {
"foo": 8
},
"remove": [ "frob" ]
}
}
}
The Razor server makes sure that invocations of hook scripts are serialized; for any hook, events are processed one-by-one to make it possible to provide transactional safety around the changes any event script might make.
Most events are directly related to a node. The JSON input to the event
script will have a node
property which contains the representation of the
node in the same format as the API produces for node details.
The JSON output of the event script can modify the node metadata:
{
"node": {
"metadata": {
"update": {
"foo": 8
},
"remove": [ "frob" ]
}
}
}
Also note that clear
is another argument that can be used to wipe out all
metadata for the node.
The hook script must exit with exit code 0 if it succeeds; any other exit code is considered a failure of the script. Whether the failure of a script has any other effects depends on the event. A failed execution can still make updates to the hook and node objects by printing to stdout in the same way as a successful execution.
To report error details, the script should produce a JSON object with an
error
property on its stdout in addition to exiting with a non-zero exit
code. If the script exits with exit code 0 the error
property will still
be recorded, but the event's severity will not be an 'error'. The error
property should itself contain an object whose message
property is a
human-readable message; additional properties can be set. Example:
{
"error": {
"message": "connection refused by frobnicate.example.com",
"port": 2345,
...
}
}
-
node-registered
: triggered after a node has been registered, i.e. after its facts have been set for the first time by the Microkernel. -
node-bound-to-policy
: triggered after a node has been bound to a policy. The script input contains apolicy
property with the details of the policy that has been bound to the node. -
node-unbound-from-policy
: triggered after a node has been marked as uninstalled by thereinstall-node
command and thus been returned to the set of nodes available for installation. -
node-deleted
: triggered after a node has been deleted. -
node-booted
: triggered every time a node boots via iPXE. -
node-facts-changed
: triggered whenever a node changes its facts. -
node-install-finished
: triggered when a policy finishes its last step.
There are three commands which may help with writing custom hooks:
- The
run-hook
command can be used to arbitrarily execute a hook with a given node input (and policy if applicable). Note that only "node-bound-to-policy" and "node-unbound-from-policy" will include the policy when executing the hook. - The
update-hook-configuration
command can be used to reset a hook's configuration. This is helpful when you are testing that the hook's output correctly updates a hook's configuration. - The
update-node-metadata
command can be used to reset a node's metadata. This is helpful when you are testing that a hook's output correctly updates a node's metadata.
The input to the hook script will be in JSON, containing a structure like below:
{
"hook": {
"name": "counter",
"configuration": {
"value": 0
}
},
"node": {
"name": "node10",
"hw_info": {
"mac": [ "52-54-00-30-8e-45" ],
"serial": "watz0815",
"uuid": "ea7c46f8-615f-234f-c1a4-20f0d3edac3d"
},
"dhcp_mac": "52-54-00-30-8e-45",
"tags": ["compute", "anything", "any", "new"],
"facts": {
"memorysize_mb": "995.05",
"myfact": "0815",
"facterversion": "2.0.1",
"architecture": "x86_64",
"hardwaremodel": "x86_64",
"processor0": "QEMU Virtual CPU version 1.6.2",
"processorcount": "1",
"ipaddress": "192.168.100.196",
"hardwareisa": "x86_64",
"netmask": "255.255.255.0",
"uniqueid": "007f0100",
"physicalprocessorcount": "1",
"virtual": "kvm",
"is_virtual": "true",
"interfaces": "eth0,lo",
"ipaddress_eth0": "192.168.100.196",
"macaddress_eth0": "52:54:00:30:8e:45",
"netmask_eth0": "255.255.255.0",
"ipaddress_lo": "127.0.0.1",
"netmask_lo": "255.0.0.0",
"network_eth0": "192.168.100.0",
"network_lo": "127.0.0.0",
"macaddress": "52:54:00:30:8e:45",
"blockdevice_vda_size": 4294967296,
"blockdevice_vda_vendor": "0x1af4",
"blockdevices": "vda",
"bios_vendor": "Watzmann Ops",
"bios_version": "08.15",
"bios_release_date": "01/01/2011",
"manufacturer": "Watzmann BIOS",
"productname": "Bochs",
"serialnumber": "WATZ0815",
"uuid": "EA7C46F8-615F-234F-C1A4-20F0D3EDAC3D",
"type": "Other"
},
"state": {
"installed": false
},
"hostname": "client-l.watzmann.net",
"root_password": "secret",
"last_checkin": "2014-05-21T03:45:47+02:00"
},
"policy": {
"name": "client-l",
"repo": "centos-6.4",
"task": "ubuntu",
"broker": "noop",
"enabled": true,
"hostname_pattern": "client-l.watzmann.net",
"root_password": "secret",
"tags": ["client-l"],
"nodes": {
"count": 0
}
}
}
The input will contain these keys, where many will disappear if their value is null:
- hook
- id
- name
- type (this is the hook type)
- configuration
- cause (this is the event string, e.g.
node-booted
)
- node
- id
- name
- hw_info
- dhcp_mac
- tags (array of hash; use
name
for each) - facts
- metadata
- state
- installed
- installed_at
- stage
- power
- desired_power_state
- last_known_power_state
- last_power_state_update_at
- hostname
- root_password
- ipmi
- hostname
- username
- last_checkin
- policy (for
node-bound-to-policy
andnode-unbound-from-policy
events only)- id
- name
- repo (object; use
name
) - task (object; use
name
) - broker (object; use
name
) - enabled
- hostname_pattern
- root_password
- tags => (array of objects; use
name
in each) - nodes
- count
Here is an example of a basic hook that will count the number of times Razor
registers a node. Let's name the hook counter
and create a corresponding
directory for the hook type, counter.hook
, inside the hooks
directory. We
can store the current count as a configuration entry with the key count
. Thus
the configuration.yaml
file might look like this:
---
count:
description: "The current value of the counter"
default: 0
We want to write a script that runs whenever a node gets bound to
a policy, so we make a file called node-bound-to-policy
and place it in the
counter.hook
folder. We can then write this script, which reads in the
current configuration value, increments it, then returns some JSON to update
the configuration on the hook object:
#! /bin/bash
json=$(< /dev/stdin)
name=$(jq '.hook.name' <<< $json)
value=$(( $(jq '.hook.config.count' <<< $json) + 1 ))
cat <<EOF
{
"hook": {
"configuration": {
"update": {
"count": $value
}
}
},
"node": {
"metadata": {
"update": {
$name: $value
}
}
}
}
EOF
Note that this script uses jq
, a bash JSON manipulation framework.
This must be on the $PATH in order for execution to succeed.
That completes the hook type. Next, we'll create the hook object which will store the configuration via:
razor create-hook --name counter --hook-type counter
Since the configuration is absent from this creation call, the default value
of 0 in configuration.yaml
is used. Alternatively, this could be set using
--configuration count=0
or -c count=0
.
The hook is now ready to go. You can query the existing hooks in a system via
razor hooks
. To query the current value of the hook's configuration,
razor hooks counter
will show count
initially set to 0. When a node gets
bound to a policy, the node-bound-to-policy
script will be triggered,
yielding a new configuration value of 1.