Looking for a more elegant way to declare complex variables

136 views
Skip to first unread message

Behrang Saeedzadeh

unread,
Nov 19, 2017, 11:49:20 PM11/19/17
to Ansible Project

Sensu is monitoring services like Nagios and the things it should monitor can be configured with JSON check definition files.


A JSON check definition file can have one or more check definitions. Here is a simplified example:


{
  "checks": {
    "cron": {
      "command": "check-process.rb -p cron",      
      "interval": 60
    }
  }
}


Let's say I have defined the following template for generating check files:


{
  "checks": {
    {% for check_def in item.check_defs %}
        "{{ check_def.name }}": {
    	  "command": "{{ check_def.command }}",
          "interval": {{ check_def.check_interval }}
        }{{ ',' if not loop.last else '' }}
    {% endfor %}
  }
}


I apply this template a task like this:


- copy:
    content:  "{{ lookup('template', 'check_def.json.j2') | to_nice_json }}"
    dest: "{{ somewhere }}"    
  with_items: "{{ check_sets }}"
  notify: Restart Sensu


The check_sets variable is defined similar to:


  vars:
    services:
      - foo
      - bar
      - baz

  tasks:    
      set_fact:
        check_defs: >
          {{
            check_defs|default([]) + [
              {
                'name' : 'my-check-def-for' + item.name,
                'command' : 'check-process.rb',
                'command_args' : '-p ' + item
              }
            ]
          }}
      with_items: "{{ services }}"


I find this approach to defining check_defs to be ugly for several reasons:

1- It is not declarative (e.g. check_defs|default([]) as well as appending items to the list are not declarative). 

2- I don't like the string concat logic using + and using quotes around hardcoded strings 

3- For newcomers, it is not easy to understand what is going on


If I use a static list, I have to repeat some variables/values. For example:


check_defs:
	- name: my-check-def-for-foo
	  command: check-process.rb
	  command_args: -p foo


Here foo is repeated twice.


Also users might diverge from the naming convention. For example by providing:


check_defs:
	- name: foo-check
	  command: check-process.rb
	  command_args: -p foo


Is there a more elegant way to declare the check_defs variable that doesn't have any of these drawbacks?


Thanks in advance.

ma...@shankerbalan.net

unread,
Nov 20, 2017, 9:45:25 AM11/20/17
to ansible...@googlegroups.com
Hi Behrang,

Comments inline...

> On 20-Nov-2017, at 10:19 AM, Behrang Saeedzadeh <behr...@gmail.com> wrote:
>
> Sensu is monitoring services like Nagios and the things it should monitor can be configured with JSON check definition files.
>
> A JSON check definition file can have one or more check definitions. Here is a simplified example:
>
> {
> "checks": {
> "cron": {
> "command": "check-process.rb -p cron",
> "interval": 60
> }
> }
> }
>
> Let's say I have defined the following template for generating check files:
>
> {
> "checks": {
> {% for check_def in item.check_defs %}
> "{{ check_def.name }}": {
> "command": "{{ check_def.command }}",
> "interval": {{ check_def.check_interval }}
> }{{ ',' if not loop.last else '' }}
> {% endfor %}
> }
> }

<snipped a lot>

I am using Ansible to manage checks.json myself but am doing so with the Sensu module from
http://docs.ansible.com/ansible/latest/sensu_check_module.html as below:

name: Updating checks in - /etc/sensu/conf.d/{{sensu_check_filename|default(check_filename)}}
sensu_check:
#path: "{{ sensu_check_filename|default(check_filename) }}"
path: /etc/sensu/conf.d/checks.json
name: "{{ item.name }}"
command: "{{ item.command }}"
interval: "{{ item.interval|default(check_interval) }}"
metric: "{{ item.metric | default('no') }}"
handle: "{{ item.handle | default('yes') }}"
occurrences: "{{ item.occurrences | default(check_occurrences) }}"
timeout: "{{ item.timeout | default(check_timeout) }}"
dependencies: "{{ item.dependencies | default(omit) }}"
subscribers: "{{ item.subscribers|default(default_subscribers) }}"
handlers: "{{ item.handlers | default(omit) }}"
refresh: "{{ item.refresh | default(check_refresh) }}"
state: "{{ item.state | default('present') }}"
standalone: "{{ item.standalone | default('no') }}"
ttl: "{{ item.ttl | default(900) }}"
aggregate: "{{ item.aggregate | default('no') }}"
subdue_begin: "{{ item.subdue_begin | default(omit) }}"
subdue_end: "{{ item.subdue_end | default(omit) }}"
source: "{{ item.source | default(omit) }}"
low_flap_threshold: "{{ item.low_flap_threshold | default(omit) }}"
high_flap_threshold: "{{ item.high_flap_threshold | default(omit) }}"
notify:
- restart_sensu_server
- restart_sensu_client
- restart_sensu_api
with_items: "{{ _sensu_check_list|sort }}"

Is there any particular reason why you chose to use a template?

Regards.
@shankerbalan






Brian Coca

unread,
Nov 20, 2017, 9:51:30 AM11/20/17
to Ansible Project
Just a note, this task does x2 transformations it does not need to, if
you use template directly you avoid many problems.

so instead of:
- copy:
content: "{{ lookup('template', 'check_def.json.j2') | to_nice_json }}"
dest: "{{ somewhere }}"
with_items: "{{ check_sets }}"
notify: Restart Sensu

use:
- template: src=check_def.json.j2 dest={{somewhere}}
with_items: "{{ check_sets }}"
notify: Restart Sensu




--
----------
Brian Coca

Behrang Saeedzadeh

unread,
Nov 20, 2017, 5:37:15 PM11/20/17
to Ansible Project
I want to pretty print the generated JSON file. Otherwise I would have used the template module.

Behrang Saeedzadeh

unread,
Nov 20, 2017, 5:38:07 PM11/20/17
to Ansible Project
Oops. I didn't know this module existed. I am maintaining a set of scripts that was written before 2.0 and this module were released.
Reply all
Reply to author
Forward
0 new messages