Skip to content

Introduction to Plans#

A plan, simply put, is a YAML file that tells the YetiCloud client what to do. It is the core of the YetiCloud ecosystem. In this introduction we'll walk through how to create your first plan, how it works, and the moving parts you need to be aware of.

Creating Your First Plan#

name: my first plan
description: a simple first plan
enabled: true

Here we can see a simple plan file. There's not much to it, in fact, it doesn't do anything. To make it do something, we'll need to add a discovery section, like this:

Add Discovery#

name: my first plan
description: a simple first plan
enabled: true

discovery:
  enabled: true
  strategy: all
  tasks: []

Great! It still doesn't do anything, but we're getting somewhere. Now we can start to discover services.

Discovering Services#

name: my first plan
description: a simple first plan
enabled: true

discovery:
  enabled: true
  strategy: all
  tasks:
    - action: path
      name: check to see if a file exists
      args:
        path: /usr/share/my_file
        state: present

To discover servivces, we'll need to add tasks to our tasks: list. For this example, we're going to use the path action. The path action looks to see if the specified path exists or not.

Great! We've set up our first action. This action will check to see if the YetiCloud client is installed in the proper path. This plan will fail unless you create a file at /usr/share/my_file, so go ahead and create it now.

Now that we've got a discovery step set up, we can move on to monitoring our host state.

Monitoring State#

name: my first plan
description: a simple first plan
enabled: true

discovery:
  enabled: true
  strategy: all
  tasks:
    - action: path
      name: check to see if a file exists
      args:
        path: /usr/share/my_file
        state: present

supervisor:
  enabled: true
  monitor:
    strategy: all
    tasks:
      - action: hash
        name: check if our file is updated
        args:
          path: /usr/share/my_file

So, its great that we can discover if a file exists, but what if we want to see if that file changes? This is where the supervisor section comes in. Lets go ahead and add it to our plan file.

As you can see, we've added the supervisor section, the monitor sub-section, and the hash action to our plan list. The hash action creates a hash of the contents of a file at a certian path and triggers a recovery action if the file is changed. This is really useful for things like making sure configuration files are not changed.

So now that we've got our monitor plan set up, what's next? Adding a recovery step!

Automatic Remediation#

name: my first plan
description: a simple first plan
enabled: true

discovery:
  enabled: true
  strategy: any
  tasks:
    - action: path
      name: check to see if a file exists
      args:
        path: /usr/share/my_file
        state: present

supervisor:
  enabled: true
  monitor:
    strategy: any
    tasks:
      - action: hash
        name: check if our file is updated
        args:
          path: /usr/share/my_file

  recovery:
    strategy: any
    tasks:
      - action: log
        name: let us know if the file changed
        args:
          text: our file at /usr/share/my_file has changed!

Now that we have our monitor defined, we can tell the client what to do if that monitor fails. We do this in the recovery sub-section of the supervisor section, like this:

As you can see, we've added a log action to our recovery plans list. This will only be triggered if the plans in the monitor section fail (according to the strategy parameter). Because we're just having some fun and don't want to actually send any alerts, we'll just use the log action for now.

If you were to put this plan file on a server and run it, assuming that /usr/share/my_file exists, you would see that the plan is successfully discovered, run, and the hash action added to the monitoring queue. And, on updating the file my_file, the log recovery action would be triggered, outputting a log into the client logs with our custom text.

Congrats! You've just completed your first YetiCloud plan file! Next we'll take a deeper dive into plans and how they work.

Anatomy of a Plan#

The first section of any plan is the name. Make it a good one, because you want names to be unique across a host. The plan name gets used for a lot of different things under the hood and having a unique name makes sure that everything works as its expected to. And don't worry about things like spaces, special characters, and whatnot -- they're automatically escaped for internal use -- so go ahead and make it what you want (as long as its valid YAML of course). Let's take a look at a plan header section:

Tags#

You can supply tags for each plan, and those tags will show up in the web UI, making it an easy way to categorize your plans. Tags are simply a key: value pairing, like this:

tags: 
  - key1: value1
  - key2: value2

Variables#

Environment Variables#

There are two kinds of environment variables: ones that are defined by the host and ones that are defined in a plan.

You can use host environment variables in your plans by referencing them with the pattern @ENV_VARIABLE_NAME.

The env section lets you define variables that can be re-used across the entire plan file. This is very helpful for defining server ports, usernames, IP addresses, the sorts of things that might get used across a number of actions. Environment variables are defined just like tags:

env:
  - ip: "10.0.0.1"
  - username: "admin"

Environment variables are referened by the env prefix, as such:

- action: dns
  name: webserver
  expect: "{{ env.ip }}"
  args:
    host: webserver.local
    type: A

Registered Variables#

In addition to predefined environment variables you can also "register" variables on the fly by supplying the register keyword and a variable name when defining an action. Like this:

- action: http_status
  name: is yeticloud up
  expect: 200
  register: yeticloud
  args:
    url: https://yeticloud.io/

Once that action is executed, the variable yeticloud now contains information about the action result. In the case of the http_status action, the yeticloud variable has the following properties:

  • status_code -- the status code returned
  • status -- a string version of the status (ex. "200 OK")
  • body -- the response body returned

The yeticloud variable can now be used elsewhere in the plan, like such:

- action: log
  name: write a log with the status code
  args:
    text: yeticloud.io retured a status code of {{ yeticloud.status_code }}

We've also built in a neat feature: any JSON that gets returned to a plan with a registered variable, that variable will have the JSON data available to it. Let's look how it works.

For the sake of our scenario, lets imagine the is yeticloud up action from above returned a JSON response like this:

{
  "status" : 200,
  "uptime" : "4d 16h 53.02m",
  "app": {
    "version" : "0.2.3",
    "build_time" : "2019-05-25T00:33:00+00:00"
  }
}

We can then use the values from this JSON response later in our plan file. Lets say we wanted to also log the version of our server, we can amend the log action to include this data:

- action: log
  name: write a log with the status code and version
  args:
    text: yeticloud.io version {{ yeticloud.app.version }} returned status code of {{ yeticloud.status_code }}

Discovery#

Plans#

Supervisor#

Monitor#

Recovery#

Metadata#

Advanced Concepts#

Monitoring Remote Hosts#

Automating Workflows#