|
|
# YAML File Structures
|
|
|
|
|
|
This is an example of a minimal Hyperion system configuration:
|
|
|
```yaml
|
|
|
name: Example-Config
|
|
|
env: env/example_env.sh
|
|
|
|
|
|
groups:
|
|
|
- name: Example Group 1
|
|
|
components:
|
|
|
- name: xclock
|
|
|
cmd:
|
|
|
- start: xclock
|
|
|
- check: pgrep xclock > /dev/null
|
|
|
host: example-host-1
|
|
|
```
|
|
|
The above example describes a system named `Example-config` with a custom environment file `example_env.sh` at a relative path. It contains only one component group named `Example Group 1` which holds a single component named `xclock` to be run on a host referenced by hostname `example-host-1`. On start the component will run the shell command `xclock` and the check command of the component searches for a xclock process piping the output to /dev/null.
|
|
|
|
|
|
The mandatory fields of a ***system configuration*** are the name and a list of groups holding at least one group that contains a single component. The optional field are the following ones:
|
|
|
- the `env` entry: holds the path to a custom environment file (both relative to the system file or absolute path will work) which is sourced before component commands and for parsing environment variables in the configuration.
|
|
|
- the 'shell_path' entry: used to specify the shell executable by path. If none is given defaults to `/bin/bash`.
|
|
|
- the `verbose_checks` entry: Sets the Boolean value for verbose checks. If verbose checks are enabled, output of custom check commands is logged. Defaults to False.
|
|
|
- the 'exclude' entry: holds a list of tags. All components tagged with a tag contained in the exclude list will be ignored when components are loaded.
|
|
|
|
|
|
A ***component group*** consists of a (preferably short) name and a list of components. It has no optional fields yet.
|
|
|
|
|
|
The ***component configurations*** are the files that hold the most information about system configurations:
|
|
|
It requires a `name`, a command list (`cmd`) and a `host` to be valid. The only mandatory entry in the`cmd` list is `start`, which defines the command to be run on component startup. A `check` command can be provided that will be run as second stage of a component check (first stage is checking if the child process of the tmux window (the invoked command) is still running) and a `stop` command is also optionally run at component shutdown. ***~~Note that currently this is the only part of the configuration where the order matters: `start` needs to be the first and `check` needs to be the second element of the list!~~ UPDATE: since 3aca6d9 order of component cmds does not matter anymore.***
|
|
|
|
|
|
Since 13f0cb5 it is possible to use environment variables as hostnames to make the configuration more dynamic.
|
|
|
You only need to use `${your_env_var}` and the parser will replace it with the value in your environment.
|
|
|
|
|
|
Multiline commands are also supported by the yaml format, you only have to use [folding style](https://yaml.org/spec/1.2/spec.html#id2796251) or [literal style](https://yaml.org/spec/1.2/spec.html#id2795688).
|
|
|
|
|
|
An optional top-level field of a component is `wait`. It defines the time in seconds to wait after a component start for a successful component check until it is considered to have failed (note that a component start will return as soon as it receives a successful check status ***or*** the component wait time is reached). The default value used for wait is defined in [config.py](https://github.com/DavidPL1/Hyperion/blob/9cf1af0a27e227d416cd32024bfe6159e6516aea/hyperion/lib/util/config.py#L53) (currently 3 seconds).
|
|
|
|
|
|
***Tagging components*** is possible with the `tags` field. It holds a list of tags associated with the component. Tagging components at this point only affects behavior in combination with the `exclude` feature for system configurations.
|
|
|
|
|
|
To model ***dependencies between components*** two optional fields are at hand: `provides` and `requires` (since 2.0.0-alpha): The `provides` field lists a set of virtual resources that are provided by the component, while `requires` field: lists a set of virtual resources that are required for the component to be run. For a configuration to be valid, the set of all combined requires has to be a subset of all combined provides entries.
|
|
|
Two components are allowed to provide the same resource, if this is the case they both are direct dependencies of the requiring component. This can be used in combination with the exclude tags feature in order to make dynamic configurations that apply to different usecases (simulation, real robot, ...) and only need a change on the top-level system config to switch between them.
|
|
|
|
|
|
The yet unreleased followup version of 2.1.0 introduced `optional-requires`, a configuration field for a component that holds a list of optional requirements. Components providing those will be listed as dependency of said component and if no component at all provides a specified optional requirement, the requirement is ignored.
|
|
|
This allows to model a consistent hierarchy of dependencies that does not break if some components in between may be excluded using the exclude components feature.
|
|
|
|
|
|
The last optional field `noauto` was added in 5712297. It marks the component to never start unless it specifically is instructed by the user to do so. This can be used for tool components that are not necessary for a system to run, but still are handy to have in the user interface for occasional quick access (teleoperation, sim control, ...). To mark a component with `noauto` you simply have to add the field to the file. A value is not required but adding some symbolic value (like True) could enhance clarity of what is happening there.
|
|
|
***Note:*** It is highly discouraged to make any component dependent on a component marked with `noauto`, because it will mess up the startup process (especially if the dependent component is not marked with `noauto`: the `start_all` procedure cannot be successful in that case!)
|
|
|
|
|
|
## Component Example
|
|
|
```yaml
|
|
|
name: Component name
|
|
|
cmd:
|
|
|
- start: start shell command
|
|
|
- check: |
|
|
|
This is a literal style multiline
|
|
|
check shell command
|
|
|
If this does not work, check the indentation levels
|
|
|
tags:
|
|
|
- tag1
|
|
|
- tag2
|
|
|
- ...
|
|
|
requires:
|
|
|
- dependency1
|
|
|
- ...
|
|
|
provides:
|
|
|
- base_system
|
|
|
- ...
|
|
|
optional-requires:
|
|
|
- something_optional
|
|
|
wait: N seconds
|
|
|
noauto: This value is not necessary
|
|
|
host: hostname or ${env_var}
|
|
|
```
|
|
|
|
|
|
***ATTENTION***
|
|
|
> Tee is used to redirect the stdout and stderr of a component process to a log file, which causes to change the buffering mode of the filedescriptors, thus if buffering is not specified by the executed command, the output will not be buffered by line. This also changes the buffering on the tty (output is only shown after the process finished running) which is bad if you want to keep track of a components state by taking a look at the stdout at runtime. This can be circumvented by adding ```stdbuf -oL -eL``` in front of your (last) start command (this will enforce linebuffering for stdout and stderr). This command is included in GNU coreutils so it is preinstalled on almost any linux machine.
|
|
|
(Issue [#38](/DavidPL1/Hyperion/issues/38) tackles this problem)
|
|
|
|
|
|
|
|
|
> Currently checks are run via the subprocess module, which is an easy to use interface for creating shells and sending commands to them. Since aliases are only enabled in interactive shells and the process of spawning an interactive shell for every arbitrary shell is not that simple, aliases can not be used in check commands. For more information about this visit issue [#40](/DavidPL1/Hyperion/issues/40)
|
|
|
|
|
|
***ATTENTION***
|
|
|
|
|
|
The parser allows splitting configurations in multiple files, which not only enhances clarity but also allows reusing components for different system configurations.
|
|
|
|
|
|
To include a yaml file inside another yaml file use the `!include relative/path/to/file.yaml` markup.
|
|
|
Sadly pyyaml needs the include to be inside a field to allow appending entries in the including file.
|
|
|
So something like this won't work:
|
|
|
```yaml
|
|
|
!include nested.yaml
|
|
|
name: ¯\_(ツ)_/¯
|
|
|
``` |
|
|
\ No newline at end of file |