Welcome to timmy’s documentation!¶
Contents:
Specification¶
OpenStack Ansible-like tool for parallel node operations: two-way data transfer, log collection, remote command execution
- The tool is based on https://etherpad.openstack.org/p/openstack-diagnostics
- Should work fine in environments deployed by Fuel versions: 4.x, 5.x, 6.x, 7.0, 8.0, 9.0, 9.1, 9.2
- Operates non-destructively.
- Can be launched on any host within admin network, provided the fuel node IP is specified and access to Fuel and other nodes is possible via ssh from the local system.
- Parallel launch - only on the nodes that are ‘online’. Some filters for nodes are also available.
- Commands (from ./cmds directory) are separated according to roles (detected automatically) by the symlinks. Thus, the command list may depend on release, roles and OS. In addition, there can be some commands that run everywhere. There are also commands that are executed only on one node according to its role, using the first node of this type they encounter.
- Modular: possible to create a special package that contains only certain required commands.
- Collects log files from the nodes using filename and timestamp filters
- Packs collected data
- Checks are implemented to prevent filesystem overfilling due to log collection, appropriate error shown.
- Can be imported into other python scripts (ex. https://github.com/f3flight/timmy-customtest) and used as a transport and structure to access node parameters known to Fuel, run commands on nodes, collect outputs, etc. with ease.
General configuration¶
All default configuration values are defined in timmy/conf.py
. Timmy works with these values if no configuration file is provided.
If a configuration file is provided via -c | --config
option, it overlays the default configuration.
An example of a configuration file is timmy_data/rq/config/example.yaml
.
Some of the parameters available in configuration file:
- ssh_opts - parameters to send to ssh command directly (recommended to leave at default), such as connection timeout, etc. See
timmy/conf.py
to review defaults. - env_vars - environment variables to pass to the commands and scripts - you can use these to expand variables in commands or scripts
- fuel_ip - the IP address of the master node in the environment
- fuel_user - username to use for accessing Nailgun API
- fuel_pass - password to access Nailgun API
- fuel_tenant - Fuel Keystone tenant to use when accessing Nailgun API
- fuel_port - port to use when connecting to Fuel Nailgun API
- fuel_keystone_port - port to use when getting a Keystone token to access Nailgun API
- fuelclient - True/False - whether to use fuelclient library to access Nailgun API
- fuel_skip_proxy - True/False - ignore
http(s)_proxy
environment variables when connecting to Nailgun API - rqdir - the path to the directory containing rqfiles, scripts to execute, and filelists to pass to rsync
- rqfile - list of dicts:
- file - path to an rqfile containing actions and/or other configuration parameters
- default - True/False - this option is used to make logs_no_default work (see below). Optional.
- logs_no_default - True/False - do not collect logs defined in any rqfile for which “default” is True
- logs_days - how many past days of logs to collect. This option will set start parameter for each logs action if not defined in it.
- logs_speed_limit - True/False - enable speed limiting of log transfers (total transfer speed limit, not per-node)
- logs_speed_default - Mbit/s - used when autodetect fails
- logs_speed - Mbit/s - manually specify max bandwidth
- logs_size_coefficient - a float value used to check local free space; ‘logs size * coefficient’ must be > free space; values lower than 0.3 are not recommended and will likely cause local disk fillup during log collection
- do_print_results - print outputs of commands and scripts to stdout
- clean - True/False - erase previous results in outdir and archive_dir dir, if any
- outdir - directory to store output data. WARNING: this directory is WIPED by default at the beginning of data collection. Be careful with what you define here.
- archive_dir - directory to put resulting archives into
- timeout - timeout for SSH commands and scripts in seconds
Configuring actions¶
Actions can be configured in a separate yaml file (by default timmy_data/rq/default.yaml
is used) and / or defined in the main config file or passed via command line options -P
, -C
, -S
, -G
.
The following actions are available for definition:
- put - a list of tuples / 2-element lists: [source, destination]. Passed to
scp
like soscp source <node-ip>:destination
. Wildcards supported for source. - cmds - a list of dicts: {‘command-name’:’command-string’}. Example: {‘command-1’: ‘uptime’}. Command string is a bash string. Commands are executed in alphabetical order of their names.
- scripts - a list of elements, each of which can be a string or a dict:
- string - represents a script filename located on a local system. If filename does not contain a path separator, the script is expected to be located inside
rqdir/scripts
. Otherwise the provided path is used to access the script. Example:'./my-test-script.sh'
- dict - use this option if you need to pass variables to your script. Script parameters are not supported, but you can use env variables instead. A dict should only contain one key which is the script filename (read above), and the value is a Bash space-separated variable assignment string. Example:
'./my-test-script.sh': 'var1=123 var2="HELLO WORLD"'
- LIMITATION: if you use a script with the same name more than once for a given node, the collected output will only contain the result of the last execution.
- INFO: Scripts are not copied to the destination system - script code is passed as stdin to bash -s executed via ssh or locally. Therefore passing parameters to scripts is not supported (unlike cmds where you can write any Bash string). You can use variables in your scripts instead. Scripts are executed in the following order: all scripts without variables, sorted by their full filename, then all scripts with variables, also sorted by full filename. Therefore if the order matters, it’s better to put all scripts into the same folder and name them according to the order in which you want them executed on the same node. Mind that scripts with variables are executed after all scripts without variables. If you need to mix scripts with variables and without and maintain order, just use dict structure for all scripts, and set null as the value for those which do not need variables.
- string - represents a script filename located on a local system. If filename does not contain a path separator, the script is expected to be located inside
- files - a list of filenames to collect. passed to
scp
. Supports wildcards. - filelists - a list of filelist filenames located on a local system. Filelist is a text file containing files and directories to collect, passed to rsync. Does not support wildcards. If the filename does not contain path separator, the filelist is expected to be located inside
rqdir/filelists
. Otherwise the provided path is used to read the filelist. - logs
- path - base path to scan for logs
- include - list of regexp strings to match log files against for inclusion (if not set = include all). Optional.
- exclude - list of regexp strings to match log files against. Excludes matched files from collection. Optional.
- start - date or datetime string to collect only files modified on or after the specified time. Format -
YYYY-MM-DD
orYYYY-MM-DD HH:MM:SS
orN
where N = integer number of days (meaning last N days). Optional.
Filtering nodes¶
- soft_filter - use to skip any operations on non-matching nodes
- hard_filter - same as above but also removes non-matching nodes from NodeManager.nodes dict - useful when using timmy as a module
- Nodes can be filtered by the following parameters defined inside soft_filter and/or hard_filter:
- roles - the list of roles, ex. [‘controller’,’compute’]
- online - enabled by default to skip non-accessible nodes
- status - the list of statuses. Default: [‘ready’, ‘discover’]
- ids - the list of ids, ex. [0,5,6]
- any other attribute of Node object which is a simple type (int, float, str, etc.) or a list containing simple types
Lists match any, meaning that if any element of the filter list matches node value (if value is a list - any element in it), the node passes.
Negative filters are possible by prefacing filter parameter with no_, example: no_id = [0] will filter out Fuel.
Negative lists also match any - if any match / collision found, the node is skipped.
You can combine any number of positive and negative filters as long as their names differ (since this is a dict).
You can use both positive and negative parameters to match the same node parameter (though it does not make much sense): roles = [‘controller’, ‘compute’] no_roles = [‘compute’] This will skip computes and run only on controllers. As already said, does not make much sense :)
Parameter-based configuration¶
It is possible to define special by_<parameter-name> dicts in config to (re)define node parameters based on other parameters. For example:
by_roles:
controller:
cmds: {'check-uptime': 'uptime'}
In this example for any controller node, cmds setting will be reset to the value above. For nodes without controller role, default (none) values will be used.
Negative matches are possible via no_ prefix:
by_roles:
no_fuel:
cmds: {'check-uptime': 'uptime'}
In this example uptime command will be executed on all nodes except Fuel server.
It is also possible to define a special once_by_<parameter-name> which works similarly, but will only result in attributes being assigned to a single (first in the list) matching node. Example:
once_by_roles:
controller:
cmds: {'check-uptime': 'uptime'}
Such configuration will result in uptime being executed on only one node with controller role, not on every controller.
rqfile format¶
rqfile
format is a bit different from config. The basic difference:
config:
scripts: [a ,b, c]
by_roles:
compute:
scripts: [d, e, f]
rqfile:
scripts:
__default: [a, b, c]
by_roles:
compute: [d, e, f]
The config and rqfile definitions presented above are equivalent. It is possible to define actions in a config file using the config format, or in an rqfile using rqfile format, linking to the rqfile in config with rqfile
setting. It is also possible to define part here and part there. Mixing identical parameters in both places is not recommended - the results may be unpredictable (such a scenario has not been thoroughly tested). In general, rqfile is the preferred place to define actions.
Configuration application order¶
Configuration is assembled and applied in a specific order:
- default configuration is initialized. See
timmy/conf.py
for details. - command line parameters, if defined, are used to modify the configuration.
- rqfile, if defined (default -
rq.yaml
), is converted and injected into the configuration. At this stage the configuration is in its final form. - for every node, configuration is applied, except
once_by_
directives: - first the top-level attributes are set
- then
by_<attribute-name>
parameters are iterated to override settings and append(accumulate) actions
- for every node, configuration is applied, except
- finally
once_by_`<attribute-name>
parameters are applied - only for one matching node for any set of matching values. This is useful, for example, if you want a specific file or command from only a single node matching a specific role, like runningnova list
only on one controller.
Once you are done with the configuration, you might want to familiarize yourself with Usage.
Usage¶
NOTICE: Even though Timmy uses nice and ionice to limit impact on the cloud, you should still expect 1 core utilization both locally (where Timmy is launched) and on each node where commands are executed or logs collected. Additionally, if logs are collected, local disk (log destination directory) may get utilized significantly.
WARNING If modifying the outdir
config parameter, please first read the related warning on configuration </configuration> page.
The easiest way to launch Timmy would be running the timmy.py
script / timmy
command:
* Timmy will perform all actions defined in the default.yaml
rq-file. The file is located in timmy_data/rq
folder in Python installation directory. Specifically:
- run diagnostic scripts on all nodes, including Fuel server
- collect configuration files for all nodes
- Timmy will NOT collect log files when executed this way.
Basically, timmy.py
is a simple wrapper that launches cli.py
.
* Current page does not reference all available CLI options. Full reference for command line interface.
* You may also want to create a custom configuration for Timmy, depending on your use case.
Basic parameters:
--only-logs
only collect logs (skip files, filelists, commands and scripts)-l
,--logs
also collect logs (logs are not collected by default due to their size)-e
,--env
filter by environment ID-R
,--role
filter by role-c
,--config
use custom configuration file to overwrite defaults. Seetimmy_data/config/example.yaml
as an example-j
,--nodes-json
use json file instead of polling Fuel (to generate json file usefuel node --json
) - speeds up initialization-o
,--dest-file
the name/path for output archive, default isgeneral.tar.gz
and put into/tmp/timmy/archives
. A folder will be created if it does not exist. It’s not recommended to use/var/log
as destination because subsequent runs with log collection may cause Timmy to collect it’s own previously created files or even update them while reading from them. The general idea is that a destination directory should contain enough space to hold all collected data and should not be in collection paths.-v
,--verbose
verbose(INFO) logging. Use-vv
to enable DEBUG logging.
Shell Mode¶
Shell Mode is activated whenever any of the following parameters are used via CLI: -C
, -S
, -P
, -G
.
A mode of execution which makes the following changes:
- rqfile (
timmy_data/rq/default.yaml
by default) is skipped - Fuel node is skipped. If for some reason you need to run specific scripts/actions via Timmy on Fuel and on other nodes at the same time, create an rqfile instead (see configuration for details, see
timmy_data/rq/neutron.yaml
as an example), coupled with--rqfile
option or a custom config file to override default rqfile. - outputs of commands (specified with
-C
options) and scripts (specified with-S
) are printed on screen - any actions (cmds, scripts, files, filelists, put, except logs) and Parameter Based configuration defined in config are ignored.
The following parameters (“actions”) are available via CLI:
-C <command>
- Bash command (string) to execute on nodes. Using multiple-C
statements will produce the same result as using one with several commands separated by;
(traditional Shell syntax), but for each-C
statement a new SSH connection is established.-S <script>
- name of the Bash script file to execute on nodes (if you do not have a path separator in the filename, you need to put the file intoscripts
folder inside a path specified byrqdir
config parameter, defaults torq
. If a path separator is present, the given filename will be used directly as provided)-P <file/path> <dest>
- upload local data to nodes (wildcards supported). You must specify 2 values for each-P
switch.-G <file/path>
- download (collect) data from nodes
Logs¶
It’s possible to specify custom log collection when using CLI:
* -L <base-path> <include-regex> <exclude-regex>
, --get-logs
- specify a base path, include regex and exclude regex to collect logs. This option can be specified more than once, in this case log lists will be united. This option does not disable default log collection defined in timmy_data/rq/default.yaml
.
* --logs-no-default
- use this option of you only need logs specified via -L
.
Execution order¶
Specified actions are executed for all applicable nodes, always in the following order: 1. put 2. commands 3. scripts 4. get, filelists 5. logs
Examples¶
timmy
- run according to the default configuration and default actions. Default actions are defined intimmy_data/rq/default.yaml
. Logs are not collected.timmy -l
- run default actions and also collect logs. Such execution is similar to Fuel’s “diagnostic snapshot” action, but will finish faster and collect less logs. There is a default log collection period based on file modification time, only files modified within the last 30 days are collected.timmy -l --days 3
- same as above but only collect log files updated within the last 3 days.timmy --only-logs
- only collect logs, no actions (files, filelists, commands, scripts, put, get) performed.timmy -C 'uptime; free -m'
- check uptime and memory on all nodestimmy -G /etc/nova/nova.conf
- getnova.conf
from all nodestimmy -R controller -P package.deb '' -C 'dpkg -i package.deb' -C 'rm package.deb' -C 'dpkg -l | grep [p]ackage'
- push a package to all nodes, install it, remove the file and check that it is installed. Commands are executed in the order in which they are provided.timmy -с myconf.yaml
- use a custom config file and run the program according to it. Custom config can specify any actions, log setup, and other settings. See configuration doc for more details.
Using custom configuration file¶
If you want to perform a set of actions on the nodes without writing a long command line (or if you want to use the options only available in config), you may want to set up config file instead. An example config structure would be:
rqdir: './pacemaker-debug' # a folder which should contain any filelists and/or scripts if they are defined later, should contain folders 'filelists' and/or 'scripts'
rqfile: null # explicitly undefine rqfile to skip default filelists and scripts
hard_filter:
roles: # only execute on Fuel and controllers
- fuel
- controller
cmds: # some commands to run on all nodes (after filtering). cmds syntax is {name: value, ...}. cmds are executed in alphabetical order.
01-my-first-command: 'uptime'
02-disk-check: 'df -h'
and-also-ram: 'free -m'
logs:
path: '/var/log' # base path to search for logs
exclude: # a list of exclude regexes
- '.*' # exclude all logs by default - does not make much sense - just an example. If the intention is to not collect all logs then this 'logs' section can be removed altogether, just ensure that either rqfile is custom or 'null', or '--logs-no-default' is set via CLI / 'logs_no_default: True' set in config.
logs_days: 5 # collect only log files updated within the last 5 days
# an example of parameter-based configuration is below:
by_roles:
controller:
scripts: # I use script here to not overwrite the cmds we have already defined for all nodes
- pacemaker-debug.sh # the name of the file inside 'scripts' folder inside 'rqdir' path, which will be executed (by default) on all nodes
files:
- '/etc/coros*' # get all files from /etc/coros* wildcard path
fuel:
logs:
path: '/var/log/remote'
include: # include regexp - non-matching log files will be excluded.
- 'crmd|lrmd|corosync|pacemaker'
Then you would run timmy -l -c my-config.yaml
to execute Timmy with such config.
Instead of putting all structure in a config file you can move actions (cmds, files, filelists, scripts, logs) to an rqfile, and specify rqfile
path in config (although in this example the config-way is more compact). rqfile
structure is a bit different:
cmds: # top-level elements are node parameters, __default will be assigned to all nodes
__default:
- 01-my-first-command: 'uptime'
- 02-disk-check: 'df -h'
- and-also-ram: 'free -m'
scripts:
by_roles: # all non "__default" keys should match, "by_<parameter>"
controller:
- pacemaker-debug.sh
files:
by_roles:
controller:
- '/etc/coros*'
logs:
by_roles:
fuel:
path: '/var/log/remote'
include:
- 'crmd|lrmd|corosync|pacemaker'
__default: # again, this default section is useless, just serving as an example here.
path: '/var/log'
exclude:
- '.*'
Then the config should look like this:
rqdir: './pacemaker-debug'
rqfile:
- file: './pacemaker-rq.yaml'
hard_filter:
roles:
- fuel
- controller
And you run timmy -l -c my-config.yaml
.
Back to Index.
CLI¶
Parallel remote command execution and file manipulation tool
usage: timmy [-V] [-c CONFIG] [-o DEST_FILE] [--log-file LOG_FILE] [-e ENV]
[-r ROLE] [-i ID] [-d NUMBER] [-G PATH] [-C COMMAND] [-S SCRIPT]
[--one-way] [--max-pairs NUMBER] [-P SOURCE DESTINATION]
[-L PATH INCLUDE EXCLUDE] [--rqfile PATH] [-l]
[--logs-no-default] [--logs-speed MBIT/S] [--logs-speed-auto]
[--logs-coeff RATIO] [--only-logs] [--fake] [--fake-logs]
[--no-archive] [--no-clean] [-q] [--maxthreads NUMBER]
[--logs-maxthreads NUMBER] [-t] [-T] [-m INVENTORY MODULE] [-a]
[--offline] [-v]
Named Arguments¶
-V, –version | Print Timmy version and exit. Default: False |
-c, –config | Path to a YAML configuration file. |
-o, –dest-file | |
Output filename for the archive in tar.gz format for command outputs and collected files. Overrides “archive_” config options. If logs are collected they will be placed in the same folder (but separate archives). | |
–log-file | Redirect Timmy log to a file. |
-e, –env | Env ID. Run only on specific environment. |
-r, –role | Can be specified multiple times. Run only on the specified role. |
-i, –id | Can be specified multiple times. Run only on the node(s) with given IDs. |
-d, –days | Define log collection period in days. Timmy will collect only logs updated on or more recently then today minus the given number of days. Default - 30. |
-G, –get | Enables shell mode. Can be specified multiple times. Filemask to collect via “scp -r”. Result is placed into a folder specified by “outdir” config option. For help on shell mode, read timmy/conf.py. |
-C, –command | Enables shell mode. Can be specified multiple times. Shell command to execute. For help on shell mode, read timmy/conf.py. |
-S, –script | Enables shell mode. Can be specified multiple times. Bash script name to execute. Script must be placed in “scripts” folder inside a path specified by “rqdir” configuration parameter. For help on shell mode, read timmy/conf.py. |
–one-way | When executing scripts_all_pairs (if defined), for each pair of nodes [A, B] run client script only on A (A->B connection). Default is to run both A->B and B->A. Default: False |
–max-pairs | When executing scripts_all_pairs (if defined), limit the amount of pairs processed simultaneously. Default is to run max number of pairs possible, which is num. nodes / 2. |
-P, –put | Enables shell mode. Can be specified multiple times. Upload filemask via”scp -r” to node(s). Each argument must contain two strings - source file/path/mask and dest. file/path. For help on shell mode, read timmy/conf.py. |
-L, –get-logs | Define specific logs to collect. Implies “-l”. Each -L option requires 3 values in the following order: path, include, exclude. See configuration doc for details on each of these parameters. Values except path can be skipped by passing empty strings. Example: -L “/var/mylogs/” “” “exclude-string” |
–rqfile | Can be specified multiple times. Path to rqfile(s) in yaml format, overrides default. |
-l, –logs | Collect logs from nodes. Logs are not collected by default due to their size. Default: False |
–logs-no-default | |
Do not use default log collection parameters, only use what has been provided either via -L or in rqfile(s). Implies “-l”. Default: False | |
–logs-speed | Limit log collection bandwidth to 90% of the specified speed in Mbit/s. |
–logs-speed-auto | |
Limit log collection bandwidth to 90% of local admin interface speed. If speed detection fails, a default value will be used. See “logs_speed_default” in conf.py. Default: False | |
–logs-coeff | Estimated logs compression ratio - this value is used during free space check. Set to a lower value (default - 1.05) to collect logs of a total size larger than locally available. Values lower than 0.3 are not recommended and may result in filling up local disk. |
–only-logs | Only collect logs, do not run commands or collect files. Default: False |
–fake | Do not run commands and scripts Default: False |
–fake-logs | Do not collect logs, only calculate size. Default: False |
–no-archive | Do not create results archive. By default, an archive with all outputs and files is created every time you run Timmy. Default: False |
–no-clean | Do not clean previous results. Allows accumulating results across runs. Default: False |
-q, –quiet | Print only command execution results and log messages. Good for quick runs / “watch” wrap. This option disables any -v parameters. Default: False |
–maxthreads | Maximum simultaneous nodes for commandexecution. |
–logs-maxthreads | |
Maximum simultaneous nodes for log collection. | |
-t, –outputs-timestamp | |
Add timestamp to outputs - allows accumulating outputs of identical commands/scripts across runs. Only makes sense with –no-clean for subsequent runs. Default: False | |
-T, –dir-timestamp | |
Add timestamp to output folders (defined by “outdir” and “archive_dir” config options). Makes each run store results in new folders. This way Timmy will always preserve previous results. Do not forget to clean up the results manually when using this option. Default: False | |
-m, –module | Use module to get node data Default: “fuel” |
-a, –analyze | Analyze collected outputs to determine node orservice health and print results Default: False |
–offline | Mark all nodes as offline, do not perform anyoperations on the nodes Default: False |
-v, –verbose | This works for -vvvv, -vvv, -vv, -v, -v -v,etc, If no -v then logging.WARNING is selected if more -v are provided it will step to INFO and DEBUG unless the option -q(–quiet) is specified Default: 0 |
Fuel module parameters
usage: timmy [-h] [--fuel-ip FUEL_IP] [--fuel-user FUEL_USER]
[--fuel-pass FUEL_PASS] [--fuel-token FUEL_TOKEN]
[--fuel-logs-no-remote] [--fuel-proxy] [-j NODES_JSON]
Named Arguments¶
–fuel-ip | fuel ip address |
–fuel-user | fuel username |
–fuel-pass | fuel password |
–fuel-token | fuel auth token |
–fuel-logs-no-remote | |
Do not collect remote logs from Fuel. Default: False | |
–fuel-proxy | use os system proxy variables for fuelclient Default: False |
-j, –nodes-json | |
Path to a json file retrieved via “fuel node –json”. Useful to speed up initialization, skips “fuel node” call. |
Local module parameters
usage: timmy [-h] -j NODES_JSON
Named Arguments¶
-j, –nodes-json | |
Path to a json file containing host info: ip, roles, etc. |
Tools module¶
tools module
Exit Codes¶
- 2 - SIGINT (Keyboard Interrupt) caught.
- 100 - not enough free space for logs. Decrease logs coefficient via CLI or config or free up space.
- 101 - rqdir configuration parameter points to a non-existing directory.
- 102 - could not load YAML file - I/O Error.
- 103 - could not load YAML file - Value Error, see log for details.
- 104 - could not load YAML file - Parser Error - incorrectly formatted YAML.
- 105 - could not retrieve information about nodes by any available means.
- 106 - fuel_ip configuration parameter not defined.
- 107 - could not load JSON file - I/O Error.
- 108 - could not load JSON file - Value Error, see log for details.
- 109 - subprocess (one of the node execution processes) exited with a Python exception.
- 110 - unable to create a directory.
- 111 - ip address must be defined for Node instance.
- 112 - one of the two parameters fuel_user or fuel_pass specified without the other.
- 113 - unhandled Python exception occured in main process.