Using Hooks

Overview

You can use hooks in SolusVM to automatically run custom scripts before or after specific events take place.

In a nutshell, here is how hooks in SolusVM work:

  • You can create a special directory on the management node and/or any compute resources. You can have a hook tied to a specific event behave differently on compute resources A and B, and be absent altogether from compute resource C.

  • In this directory, you need to place one or more executable files containing a custom script. Custom scripts can be written in Bash, PHP, Python, Golang, Java, or any other programming language able to parse a JSON file passed to it.

  • Every time one of the supported events, such as a virtual server being rebooted, takes place for a virtual server, SolusVM executes all custom scripts found on the management node or compute resource hosting that virtual server. You can find the complete list of supported events further in this topic. You cannot create hooks for unsupported events. Every configured hook is triggered twice per event: before and after the event takes place. Hooks are triggered regardless of whether a supported event is initiated via the GUI, CLI, or API.

  • When a hook is triggered, SolusVM passes to it a JSON object containing the following information: the type of the event, whether the hook was triggered before or after the event, and also certain relevant data, such as the ID of the virtual server in question.

For example, when a new virtual server is being created, the virtual server creation event is triggered. It passes, among other data, the newly created virtual server’s IP address. You can create a custom script that would parse the incoming JSON object, take that data, and use it to create the necessary firewall rules on the compute resource hosting it.

Creating a Hook

We recommend using a single file as the starting point, which will determine which other scripts or commands to execute.

To create a hook on the management node or compute resource:

  1. Log in to the management node or compute resource via SSH as the root user.

  2. Create a directory named /usr/local/solus/hooks/ if it does not exist.

    mkdir /usr/local/solus/hooks/

  3. Create a file and paste your custom script into the file. You can give the file any name you want, for example, hook.sh.

    touch /usr/local/solus/hooks/hook.sh

  4. Make the file executable.

    chmod +x /usr/local/solus/hooks/hook.sh

Your hook is now created. SolusVM runs the executable every time a supported event takes place. You cannot configure SolusVM to only run specific executables for specific events. The custom script must be able to parse the JSON object and perform certain actions depending on its contents.

When a supported event takes place, all configured hooks (if any) are triggered. The action is paused on the SolusVM side until all custom scripts have exited, or until one minute has passed, whichever comes first. So, for example, here is what happens when a virtual server is restarted, and there are one or more hooks configured on the management node or compute resource hosting that virtual server:

  1. The command to restart the virtual server is given via the GUI, CLI, or API.
  2. SolusVM runs all executables in the /usr/local/solus/hooks/ directory, passing to each one the JSON object containing all relevant information, including the supported event ID and the pre stage indicator.
  3. All executables exit or a minute passes.
  4. The virtual server is restarted.
  5. SolusVM runs all executables in the /usr/local/solus/hooks/ directory, passing to each one the JSON object containing all relevant information, including the supported event ID and the post stage indicator.

Note:

Do not place any files other than hooks in the /usr/local/solus/hooks/ directory.

JSON Object Anatomy

Your hook must expect data on stdin (standard input) in valid JSON format. Every time a triggering event takes place, a JSON object containing all relevant data is passed to every executable in the /usr/local/solus/hooks/ directory. Here is what this object may look like:

{
    "action": "server-restart",
    "stage": "pre",
    "data": {
      "uuid": "c7ee19aa-2722-4139-9223-60ed4baf09e2",
      "virtualization_type": "kvm"
    }
  }

Here, action specifies what kind of event triggered the hook (in this case, a virtual server being restarted), stage specifies whether the event is about to happen or has just happened (in this case, the former), and data specifies information relevant to the event (in this case, virtual server ID and its virtualization type). So, this object indicates that the KVM based virtual server with the ID c7ee19aa-2722-4139-9223-60ed4baf09e2 is about to be restarted, and the custom script called by the hook can, for example, be used to notify the virtual server’s owner.

The action, stage, and data keys are always present.

  • The stage key can only have the pre and post values.

  • The action key can have a number of values corresponding to the supported events.

    • server-create
    • server-delete
    • server-migrate
    • server-reinstall
    • server-restart
    • server-start
    • server-stop
    • server-network-create
    • server-network-delete
    • server-network-update
  • The data object includes a number of keys that can have different values. One or more specific keys correspond to each specific action key. For example, a JSON object corresponding to a virtual server being restarted (the server-restart action) will always include the uuid and virtualization_type keys within the data object, and never any others.

Note:

A single event may trigger multiple hooks. For example, creating a new virtual server triggers both the server-create and the server-network-create hooks.

Note:

If an event fails during execution (for example, a virtual server cannot be created), only the pre hooks will be triggered, but not the post hooks.

Hook Examples

Bash example

  1. Log in to the management node or compute resource via SSH as the root user.

  2. Create a directory named /usr/local/solus/hooks/ if it does not exist.

    mkdir /usr/local/solus/hooks/

  3. Create a file.

    touch /usr/local/solus/hooks/hook.sh

  4. Make it executable.

    chmod +x /usr/local/solus/hooks/hook.sh

  5. Paste the following content into the file.

    #!/bin/bash
    set -xEeuo pipefail
    
    tfile=$(mktemp /tmp/hook_test_$(date "+%Y-%m-%d-%H-%M-%S-%N")_XXX)
    
    cat /dev/stdin | jq > $tfile
  6. Install the jq program on the server.

    apt install jq or dnf install jq

Python example

  1. Log in to the management node or compute resource via SSH as the root user.

  2. Create a directory named /usr/local/solus/hooks/ if it does not exist.

    mkdir /usr/local/solus/hooks/

  3. Create a file.

    touch /usr/local/solus/hooks/hook.py

  4. Make it executable.

    chmod +x /usr/local/solus/hooks/hook.py

  5. Paste the following content into the file.

    #!/usr/bin/env python3
    import sys
    import json
    
    ACTION_SERVER_RESTART = "server-restart"
    STAGE_PRE = "pre"
    STAGE_POST = "post"
    
    event = json.loads(sys.stdin.read())
    
    
    def handle_server_restart_pre(event_data):
        print(f'Server going restart: {event_data["uuid"]}')
    
    
    def handle_server_restart_post(event_data):
        print(f'Server was restarted: {event_data["uuid"]}')
    
    
    action = event["action"]
    stage = event["stage"]
    data = event["data"]
    
    if ACTION_SERVER_RESTART == action:
        if STAGE_PRE == stage:
            handle_server_restart_pre(data)
            sys.exit(0)
        if STAGE_POST == stage:
            handle_server_restart_post(data)
            sys.exit(0)
  6. Install the json module on the server.

    python -c "help('modules')"

Testing your hook

  1. Log in to the management node or compute resource via SSH as the root user.

  2. Pass a JSON object to your hook, for example:

    echo '{
    "action": "server-restart",
    "stage": "pre",
    "data":
    
    { "uuid": "c7ee19aa-2722-4139-9223-60ed4baf09e2", "virtualization_type": "kvm" }
    }' | /usr/local/solus/hooks/hook.sh