diff --git a/changelogs/fragments/enhance-icinga2-objects.yml b/changelogs/fragments/enhance-icinga2-objects.yml new file mode 100644 index 00000000..5e551335 --- /dev/null +++ b/changelogs/fragments/enhance-icinga2-objects.yml @@ -0,0 +1,22 @@ +major_changes: + - | + The performance of the action plugin :code:`icinga2_object` has been greatly improved. + Instead of writing individual objects to files and later merging them, + they are instead now merged in memory on a per destination basis. + This means that configuration files no longer have to be assembled after the fact. + + This also drops the :code:`order` parameter previously used to define the order in which + objects are written if they belong to the same destination file. + The new behavior only changes the order in the files but does not change the end result. + + A performance gain of up to 80% has been seen in testing. + +known_issues: + - | + With the changes in :code:`icinga2_object` arises a problem. + The prior directory structure within :code:`/var/tmp/icinga/` does not fit the new approach for writing configuration files. + Some paths that would become directories before are now treated as files. + If the old directory structure is present on a remote host, deployment with the new method will most likely fail due to this. + + If the execution of :code:`icinga2_object` fails, deleting `/var/tmp/icinga/` should fix the problem. + This, however, is a manual step that needs to be done. diff --git a/plugins/action/icinga2_object.py b/plugins/action/icinga2_object.py index 582c7f82..1310131d 100644 --- a/plugins/action/icinga2_object.py +++ b/plugins/action/icinga2_object.py @@ -4,6 +4,7 @@ from ansible.errors import AnsibleError from ansible.plugins.action import ActionBase from ansible.utils.vars import merge_hash +from ansible.utils.display import Display from ansible_collections.netways.icinga.plugins.module_utils.parse import Icinga2Parser @@ -13,111 +14,133 @@ def run(self, tmp=None, task_vars=None): result = super(ActionModule, self).run(tmp, task_vars) - args = dict() - args = self._task.args.copy() - args = merge_hash(args.pop('args', {}), args) - object_type = args.pop('type', None) - - if object_type not in task_vars['icinga2_object_types']: - raise AnsibleError('unknown Icinga object type: %s' % object_type) - - # - # distribute to object type as module (name: icinga2_type) - # - obj = dict() - obj = self._execute_module( - module_name='icinga2_'+object_type.lower(), - module_args=args, - task_vars=task_vars, - tmp=tmp - ) - - if 'failed' in obj: - raise AnsibleError('Call to module failed: %s' % obj['msg']) - if 'skipped' in obj and obj['skipped']: - raise AnsibleError('Call to module was skipped: %s' % obj['msg']) - - # - # file path handling for assemble - # - path = task_vars['icinga2_fragments_path'] + '/' + obj['file'] + '/' - file_fragment = path + obj['order'] + '_' + object_type.lower() + '-' + obj['name'] - - if obj['state'] != 'absent': - file_args = dict() - file_args['state'] = 'directory' - file_args['path'] = path - file_module = self._execute_module( - module_name='file', - module_args=file_args, - task_vars=task_vars, - tmp=tmp + arguments = dict() + arguments = self._task.args.copy() + + # Create dict to bundle objects that will end up in the same file + destinations = dict() + + # Deprecate order key + display = Display() + if [x for x in arguments['objects'] if 'order' in x]: + display.deprecated( + 'The \'order\' parameter for different object types is deprecated. It no longer has any effect.' ) - result = merge_hash(result, file_module) - varlist = list() # list of variables from 'apply for' + for args in arguments['objects']: + args = merge_hash(args.pop('args', {}), args) + object_type = args.pop('type', None) + + if object_type not in task_vars['icinga2_object_types']: + raise AnsibleError('unknown Icinga object type: %s' % object_type) # - # quoting of object name? + # distribute to object type as module (name: icinga2_type) # - if obj['name'] not in task_vars['icinga2_combined_constants']: - object_name = '"' + obj['name'] + '"' - else: - object_name = obj['name'] + obj = dict() + obj = self._execute_module( + module_name='icinga2_'+object_type.lower(), + module_args=args, + task_vars=task_vars, + tmp=tmp + ) + + if 'failed' in obj: + raise AnsibleError('Call to module failed: %s' % obj['msg']) + if 'skipped' in obj and obj['skipped']: + raise AnsibleError('Call to module was skipped: %s' % obj['msg']) # - # apply rule? + # file path handling for assemble # - if 'apply' in obj and obj['apply'] and not obj['args']['assign']: - raise AnsibleError('Apply rule %s is missing the assign rule.' % obj['name']) - if 'apply' in obj and obj['apply']: - object_content = 'apply ' + object_type - if 'apply_target' in obj and obj['apply_target']: - object_content += ' ' + object_name + ' to ' + obj['apply_target'] - elif 'apply_for' in obj and obj['apply_for']: - object_content += ' for (' + obj['apply_for'] + ') ' - r = re.search(r'^(.+)\s+in\s+', obj['apply_for']) - if r: - tmp = r.group(1).strip() - r = re.search(r'^(.+)=>(.+)$', tmp) + path = task_vars['icinga2_fragments_path'] + '/' + obj['file'] + if path not in destinations: + destinations[path] = list() + + if obj['state'] != 'absent': + varlist = list() # list of variables from 'apply for' + + # + # quoting of object name? + # + if obj['name'] not in task_vars['icinga2_combined_constants']: + object_name = '"' + obj['name'] + '"' + else: + object_name = obj['name'] + + # + # apply rule? + # + if 'apply' in obj and obj['apply'] and not obj['args']['assign']: + raise AnsibleError('Apply rule %s is missing the assign rule.' % obj['name']) + if 'apply' in obj and obj['apply']: + object_content = 'apply ' + object_type + if 'apply_target' in obj and obj['apply_target']: + object_content += ' ' + object_name + ' to ' + obj['apply_target'] + elif 'apply_for' in obj and obj['apply_for']: + object_content += ' for (' + obj['apply_for'] + ') ' + r = re.search(r'^(.+)\s+in\s+', obj['apply_for']) if r: - varlist.extend([r.group(1).strip(), r.group(2).strip()]) - else: - varlist.append(tmp) + tmp = r.group(1).strip() + r = re.search(r'^(.+)=>(.+)$', tmp) + if r: + varlist.extend([r.group(1).strip(), r.group(2).strip()]) + else: + varlist.append(tmp) + else: + object_content += ' ' + object_name + # + # template? + # + elif 'template' in obj and obj['template']: + object_content = 'template ' + object_type + ' ' + object_name + # + # object + # else: - object_content += ' ' + object_name - # - # template? - # - elif 'template' in obj and obj['template']: - object_content = 'template ' + object_type + ' ' + object_name - # - # object - # - else: - object_content = 'object ' + object_type + ' ' + object_name - object_content += ' {\n' + object_content = 'object ' + object_type + ' ' + object_name + object_content += ' {\n' - # - # imports? - # - if 'imports' in obj: - for item in obj['imports']: - object_content += ' import "' + str(item) + '"\n' - object_content += '\n' + # + # imports? + # + if 'imports' in obj: + for item in obj['imports']: + object_content += ' import "' + str(item) + '"\n' + object_content += '\n' + + # + # parser + # + object_content += Icinga2Parser().parse(obj['args'], list(task_vars['icinga2_combined_constants'].keys())+task_vars['icinga2_reserved']+varlist+list(obj['args'].keys()), 2) + '}\n' + destinations[path] += [object_content] + + + + for destination, objects in destinations.items(): + # Remove duplicate entries and sort list to ensure idempotency + objects = list(set(objects)) + objects.sort() + + config_string = '\n\n'.join(objects) + + file_args = dict() + file_args['state'] = 'directory' + file_args['path'] = '/'.join(destination.split('/')[:-1]) + file_module = self._execute_module( + module_name='file', + module_args=file_args, + task_vars=task_vars, + tmp=tmp + ) + result = merge_hash(result, file_module) + + varlist = list() # list of variables from 'apply for' - # - # parser - # - object_content += Icinga2Parser().parse( - obj['args'], - list(task_vars['icinga2_combined_constants'].keys()) + task_vars['icinga2_reserved'] + varlist + list(obj['args'].keys()), - 2 - ) + '}\n' copy_action = self._task.copy() copy_action.args = dict() - copy_action.args['dest'] = file_fragment - copy_action.args['content'] = object_content + copy_action.args['dest'] = destination + copy_action.args['content'] = config_string copy_action = self._shared_loader_obj.action_loader.get( 'copy', @@ -130,19 +153,6 @@ def run(self, tmp=None, task_vars=None): ) result = merge_hash(result, copy_action.run(task_vars=task_vars)) - else: - # remove file if does not belong to a feature - if 'features-available' not in path: - file_args = dict() - file_args['state'] = 'absent' - file_args['path'] = file_fragment - file_module = self._execute_module( - module_name='file', - module_args=file_args, - task_vars=task_vars, - tmp=tmp - ) - result = merge_hash(result, file_module) - result['dest'] = file_fragment + result['destinations'] = list(destinations.keys()) return result diff --git a/roles/icinga2/defaults/main.yml b/roles/icinga2/defaults/main.yml index 20d499b2..ebea8583 100644 --- a/roles/icinga2/defaults/main.yml +++ b/roles/icinga2/defaults/main.yml @@ -14,6 +14,7 @@ icinga2_features: - name: checker - name: notification - name: mainlog +icinga2_objects: [] icinga2_remote_objects: [] _icinga2_custom_conf_paths: [] icinga2_config_host: "{{ ansible_fqdn }}" diff --git a/roles/icinga2/tasks/configure.yml b/roles/icinga2/tasks/configure.yml index c7b5b2c0..5f79f2a0 100644 --- a/roles/icinga2/tasks/configure.yml +++ b/roles/icinga2/tasks/configure.yml @@ -41,7 +41,7 @@ loop: "{{ icinga2_config_directories }}" when: - icinga2_config_directories is defined - - item.split('/')[0] == 'conf.d' or item.split('/')[0] == 'zones.d' or item.split('/')[0] == icinga2_confd + - item.split('/')[0] == 'conf.d' or item.split('/')[0] == 'zones.d' or item.split('/')[0] == icinga2_confd.split('/')[0] # If multiple local configs folders can be defined this rule creates only # the directories starting with defined folders. # Related var looks like this: @@ -50,14 +50,14 @@ # - my_own_config.d #- item.split('/')[0] in icinga2_local_config or item.split('/')[0] == 'zones.d' -- name: collect config fragments +- name: collect config fragments (icinga2_fragments_path) find: path: "{{ icinga2_fragments_path }}" recurse: yes file_type: file register: result_frag -- name: cleanup config files +- name: cleanup config files (icinga2_fragments_path) file: state: absent dest: "{{ item.path }}" @@ -68,32 +68,70 @@ - item.path not in icinga2_local_objects - item.path not in _icinga2_custom_conf_paths -- name: collect empty config dirs - shell: >- - find {{ icinga2_fragments_path }} -mindepth 1 -type d -empty - register: _empty_result - check_mode: false - changed_when: _empty_result.stdout_lines |length > 0 +- name: Remove empty config dirs (icinga2_fragments_path) + ansible.builtin.command: + cmd: "find {{ icinga2_fragments_path }} -mindepth 2 -type d -empty -print -exec rmdir {} +" + register: _removed_dirs + until: _removed_dirs.stdout_lines | length == 0 + retries: 100 + delay: 0 + changed_when: _removed_dirs.attempts > 1 -- name: remove empty config dirs - file: +- name: Collect config files (icinga2_config_path) + vars: + _icinga2_config_directories: "{{ + (icinga2_config_directories | select('match', '^conf.d/.*')) + + (icinga2_config_directories | select('match', '^zones.d/.*')) + + (icinga2_config_directories | select('match', icinga2_confd)) + }}" + ansible.builtin.find: + paths: "{{ _icinga2_config_directories | netways.icinga.prefix(prefix=icinga2_config_path + '/') }}" + recurse: true + file_type: file + pattern: "*.conf" + register: result + +- name: Cleanup config files (icinga2_config_path) + loop: "{{ result.files | map(attribute='path') }}" + vars: + _icinga2_sys_config_fragment_path: "{{ item | regex_replace('^' + icinga2_config_path + '(.*)$', icinga2_fragments_path + '\\1') }}" + when: + - _icinga2_sys_config_fragment_path not in icinga2_local_objects + - _icinga2_sys_config_fragment_path not in _icinga2_custom_conf_paths + ansible.builtin.file: state: absent - path: "{{ item }}" - loop: "{{ _empty_result.stdout_lines }}" + dest: "{{ item }}" + +- name: Remove empty config dirs (icinga2_config_path) + vars: + _icinga2_config_directories: "{{ + ( + (icinga2_config_directories | select('match', '^conf.d/.*')) + + (icinga2_config_directories | select('match', '^zones.d/.*')) + + (icinga2_config_directories | select('match', icinga2_confd)) + ) | netways.icinga.prefix(prefix=icinga2_config_path + '/') | join(' ') + }}" + ansible.builtin.command: + cmd: "find {{ _icinga2_config_directories }} -mindepth 1 -type d -empty -print -exec rmdir {} +" + register: _removed_dirs + until: _removed_dirs.stdout_lines | length == 0 + retries: 100 + delay: 0 + changed_when: _removed_dirs.attempts > 1 - name: collect config files find: path: "{{ icinga2_fragments_path }}" recurse: yes - file_type: directory + file_type: file pattern: '*.conf' register: result - name: assemble config files - ansible.builtin.assemble: + ansible.builtin.copy: src: "{{ item.path }}" - dest: "{{ item.path |regex_replace('^'+icinga2_fragments_path, '/etc/icinga2') }}" - delimiter: ' ' + remote_src: true + dest: "{{ item.path | regex_replace('^'+icinga2_fragments_path, icinga2_config_path) }}" owner: "{{ icinga2_user }}" group: "{{ icinga2_group }}" mode: 0644 @@ -111,11 +149,3 @@ loop_control: label: "{{ item.name }}" notify: check-and-reload-icinga2-service - -- name: remove empty config files - ansible.builtin.file: - state: absent - path: "{{ item |regex_replace('^'+icinga2_fragments_path, '/etc/icinga2') }}" - when: item.split('/')[icinga2_fragments_path.split('/')|length] == 'conf.d' or item.split('/')[icinga2_fragments_path.split('/')|length] == 'zones.d' - loop: "{{ _empty_result.stdout_lines }}" - notify: check-and-reload-icinga2-service diff --git a/roles/icinga2/tasks/features/api.yml b/roles/icinga2/tasks/features/api.yml index 9837b98e..7ae83da1 100644 --- a/roles/icinga2/tasks/features/api.yml +++ b/roles/icinga2/tasks/features/api.yml @@ -24,53 +24,44 @@ set_fact: args: "{{ args|default({}) | combine({idx.key: idx.value}) }}" when: idx.key not in ['ca_host', 'ca_host_port', 'cert_name', 'ca_fingerprint', 'force_newcert', 'zones', 'endpoints', 'ssl_cacert', 'ssl_key', 'ssl_cert', 'ssl_remote_source', 'ticket_salt' ] - loop: "{{ icinga2_dict_features.api |dict2items }}" + loop: "{{ icinga2_dict_features.api | dict2items }}" loop_control: loop_var: idx - name: feature api ApiListener object - icinga2_object: - name: api - type: ApiListener - file: features-available/api.conf - args: "{{ args }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: api + type: ApiListener + file: features-available/api.conf + args: "{{ args }}" - name: feature api Endpoint objects - icinga2_object: - type: Endpoint - args: "{{ idx }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - type: Endpoint + args: "{{ idx }}" loop: "{{ icinga2_endpoints }}" loop_control: loop_var: idx register: result -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [idx.dest] }}" - loop: "{{ result.results }}" - loop_control: - loop_var: idx - label: "Add '{{ idx.path }}' to local objects" - - name: feature api Zone objects - icinga2_object: - type: Zone - args: "{{ idx }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - type: Zone + args: "{{ idx }}" loop: "{{ icinga2_zones }}" loop_control: loop_var: idx register: result -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [idx.dest] }}" - loop: "{{ result.results }}" - loop_control: - loop_var: idx - label: "Add '{{ idx.path }}' to local objects" - - name: create new CA block: - name: check ca key already exists diff --git a/roles/icinga2/tasks/features/checker.yml b/roles/icinga2/tasks/features/checker.yml index 54011c56..9229f7ec 100644 --- a/roles/icinga2/tasks/features/checker.yml +++ b/roles/icinga2/tasks/features/checker.yml @@ -1,12 +1,11 @@ --- - name: feature checker CheckerComponent object - icinga2_object: - name: checker - type: CheckerComponent - file: features-available/checker.conf - args: "{{ icinga2_dict_features.checker }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: checker + type: CheckerComponent + file: features-available/checker.conf + args: "{{ icinga2_dict_features.checker }}" diff --git a/roles/icinga2/tasks/features/command.yml b/roles/icinga2/tasks/features/command.yml index 0a1997e1..dbd136c2 100644 --- a/roles/icinga2/tasks/features/command.yml +++ b/roles/icinga2/tasks/features/command.yml @@ -1,12 +1,11 @@ --- - name: feature command ExternalCommandListener object - icinga2_object: - name: command - type: ExternalCommandListener - file: features-available/command.conf - args: "{{ icinga2_dict_features.command }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: command + type: ExternalCommandListener + file: features-available/command.conf + args: "{{ icinga2_dict_features.command }}" diff --git a/roles/icinga2/tasks/features/compatlog.yml b/roles/icinga2/tasks/features/compatlog.yml index 5c05980e..53799054 100644 --- a/roles/icinga2/tasks/features/compatlog.yml +++ b/roles/icinga2/tasks/features/compatlog.yml @@ -1,12 +1,12 @@ --- - name: Feature compatlog CompatLogger object - netways.icinga.icinga2_object: - name: compatlog - type: CompatLogger - file: features-available/compatlog.conf - args: "{{ icinga2_dict_features.compatlog }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: compatlog + type: CompatLogger + file: features-available/compatlog.conf + args: "{{ icinga2_dict_features.compatlog }}" register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" diff --git a/roles/icinga2/tasks/features/debuglog.yml b/roles/icinga2/tasks/features/debuglog.yml index e8824d18..b1228eec 100644 --- a/roles/icinga2/tasks/features/debuglog.yml +++ b/roles/icinga2/tasks/features/debuglog.yml @@ -1,14 +1,13 @@ --- - name: feature debuglog FileLogger object - icinga2_object: - name: debug-file - type: FileLogger - file: features-available/debuglog.conf - path: LogDir + /debug.log - severity: debug - args: "{{ icinga2_dict_features.debuglog }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: debug-file + type: FileLogger + file: features-available/debuglog.conf + path: LogDir + /debug.log + severity: debug + args: "{{ icinga2_dict_features.debuglog }}" diff --git a/roles/icinga2/tasks/features/elasticsearch.yml b/roles/icinga2/tasks/features/elasticsearch.yml index e310134f..467e1b00 100644 --- a/roles/icinga2/tasks/features/elasticsearch.yml +++ b/roles/icinga2/tasks/features/elasticsearch.yml @@ -1,12 +1,11 @@ --- - name: feature elasticsearch ElasticsearchWriter object - icinga2_object: - name: elasticsearch - type: ElasticsearchWriter - file: features-available/elasticsearch.conf - args: "{{ icinga2_dict_features.elasticsearch }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: elasticsearch + type: ElasticsearchWriter + file: features-available/elasticsearch.conf + args: "{{ icinga2_dict_features.elasticsearch }}" diff --git a/roles/icinga2/tasks/features/gelf.yml b/roles/icinga2/tasks/features/gelf.yml index 663a3e66..428b5cac 100644 --- a/roles/icinga2/tasks/features/gelf.yml +++ b/roles/icinga2/tasks/features/gelf.yml @@ -1,12 +1,11 @@ --- - name: feature influxdb GelfWriter object - icinga2_object: - name: gelf - type: GelfWriter - file: features-available/gelf.conf - args: "{{ icinga2_dict_features.gelf }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: gelf + type: GelfWriter + file: features-available/gelf.conf + args: "{{ icinga2_dict_features.gelf }}" diff --git a/roles/icinga2/tasks/features/graphite.yml b/roles/icinga2/tasks/features/graphite.yml index 148b5270..6f2f46b5 100644 --- a/roles/icinga2/tasks/features/graphite.yml +++ b/roles/icinga2/tasks/features/graphite.yml @@ -1,12 +1,11 @@ --- - name: feature graphite GraphiteWriter object - icinga2_object: - name: graphite - type: GraphiteWriter - file: features-available/graphite.conf - args: "{{ icinga2_dict_features.graphite }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: graphite + type: GraphiteWriter + file: features-available/graphite.conf + args: "{{ icinga2_dict_features.graphite }}" diff --git a/roles/icinga2/tasks/features/icingadb.yml b/roles/icinga2/tasks/features/icingadb.yml index c1add7c2..b4285879 100644 --- a/roles/icinga2/tasks/features/icingadb.yml +++ b/roles/icinga2/tasks/features/icingadb.yml @@ -1,12 +1,12 @@ --- - name: feature icingadb IcingaDB object - icinga2_object: - name: icingadb - type: IcingaDB - file: features-available/icingadb.conf - args: "{{ icinga2_dict_features.icingadb }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: icingadb + type: IcingaDB + file: features-available/icingadb.conf + args: "{{ icinga2_dict_features.icingadb }}" register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" diff --git a/roles/icinga2/tasks/features/idomysql.yml b/roles/icinga2/tasks/features/idomysql.yml index f6614c62..53c77961 100644 --- a/roles/icinga2/tasks/features/idomysql.yml +++ b/roles/icinga2/tasks/features/idomysql.yml @@ -5,15 +5,14 @@ icinga2_import_schema: "{{ icinga2_dict_features.idomysql.import_schema| default(False) }}" - name: feature idomysql IdoMysqlConnection object - icinga2_object: - name: ido-mysql - type: IdoMysqlConnection - file: features-available/ido-mysql.conf - args: "{{ icinga2_dict_features.idomysql }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: ido-mysql + type: IdoMysqlConnection + file: features-available/ido-mysql.conf + args: "{{ icinga2_dict_features.idomysql }}" - name: install on {{ ansible_os_family }} include_tasks: "features/idomysql/install_on_{{ ansible_os_family }}.yml" diff --git a/roles/icinga2/tasks/features/idopgsql.yml b/roles/icinga2/tasks/features/idopgsql.yml index 9e92d845..16918383 100644 --- a/roles/icinga2/tasks/features/idopgsql.yml +++ b/roles/icinga2/tasks/features/idopgsql.yml @@ -5,15 +5,14 @@ icinga2_import_schema: "{{ icinga2_dict_features.idopgsql.import_schema| default(False) }}" - name: feature idopgsql IdoPgsqlConnection object - icinga2_object: - name: ido-pgsql - type: IdoPgsqlConnection - file: features-available/ido-pgsql.conf - args: "{{ icinga2_dict_features.idopgsql }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: ido-pgsql + type: IdoPgsqlConnection + file: features-available/ido-pgsql.conf + args: "{{ icinga2_dict_features.idopgsql }}" - name: install on {{ ansible_os_family }} include_tasks: "features/idopgsql/install_on_{{ ansible_os_family }}.yml" diff --git a/roles/icinga2/tasks/features/influxdb.yml b/roles/icinga2/tasks/features/influxdb.yml index 9be4a801..df25a075 100644 --- a/roles/icinga2/tasks/features/influxdb.yml +++ b/roles/icinga2/tasks/features/influxdb.yml @@ -1,12 +1,11 @@ --- - name: feature influxdb InfluxdbWriter object - icinga2_object: - name: influxdb - type: InfluxdbWriter - file: features-available/influxdb.conf - args: "{{ icinga2_dict_features.influxdb }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: influxdb + type: InfluxdbWriter + file: features-available/influxdb.conf + args: "{{ icinga2_dict_features.influxdb }}" diff --git a/roles/icinga2/tasks/features/influxdb2.yml b/roles/icinga2/tasks/features/influxdb2.yml index c6beabe6..ea162435 100644 --- a/roles/icinga2/tasks/features/influxdb2.yml +++ b/roles/icinga2/tasks/features/influxdb2.yml @@ -1,12 +1,11 @@ --- - name: feature influxdb2 Influxdb2Writer object - icinga2_object: - name: influxdb2 - type: Influxdb2Writer - file: features-available/influxdb2.conf - args: "{{ icinga2_dict_features.influxdb2 }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: influxdb2 + type: Influxdb2Writer + file: features-available/influxdb2.conf + args: "{{ icinga2_dict_features.influxdb2 }}" diff --git a/roles/icinga2/tasks/features/livestatus.yml b/roles/icinga2/tasks/features/livestatus.yml index f85aff6a..28f81e70 100644 --- a/roles/icinga2/tasks/features/livestatus.yml +++ b/roles/icinga2/tasks/features/livestatus.yml @@ -1,12 +1,11 @@ --- - name: feature livestatus LivestatusListener object - icinga2_object: - name: livestatus - type: LivestatusListener - file: features-available/livestatus.conf - args: "{{ icinga2_dict_features.livestatus }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: livestatus + type: LivestatusListener + file: features-available/livestatus.conf + args: "{{ icinga2_dict_features.livestatus }}" diff --git a/roles/icinga2/tasks/features/mainlog.yml b/roles/icinga2/tasks/features/mainlog.yml index 338c4455..0d80e9ef 100644 --- a/roles/icinga2/tasks/features/mainlog.yml +++ b/roles/icinga2/tasks/features/mainlog.yml @@ -1,12 +1,11 @@ --- - name: feature mainlog FileLogger object - icinga2_object: - name: main-log - type: FileLogger - file: features-available/mainlog.conf - args: "{{ icinga2_dict_features.mainlog }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: main-log + type: FileLogger + file: features-available/mainlog.conf + args: "{{ icinga2_dict_features.mainlog }}" diff --git a/roles/icinga2/tasks/features/notification.yml b/roles/icinga2/tasks/features/notification.yml index 97afc44d..fb4d1e39 100644 --- a/roles/icinga2/tasks/features/notification.yml +++ b/roles/icinga2/tasks/features/notification.yml @@ -1,12 +1,11 @@ --- - name: feature notification NotificationComponent object - icinga2_object: - name: notification - type: NotificationComponent - file: features-available/notification.conf - args: "{{ icinga2_dict_features.notification }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: notification + type: NotificationComponent + file: features-available/notification.conf + args: "{{ icinga2_dict_features.notification }}" diff --git a/roles/icinga2/tasks/features/opentsdb.yml b/roles/icinga2/tasks/features/opentsdb.yml index 1e10d9c4..e93d56dc 100644 --- a/roles/icinga2/tasks/features/opentsdb.yml +++ b/roles/icinga2/tasks/features/opentsdb.yml @@ -1,12 +1,11 @@ --- - name: feature influxdb OpenTsdbWriter object - icinga2_object: - name: opentsdb - type: OpenTsdbWriter - file: features-available/opentsdb.conf - args: "{{ icinga2_dict_features.opentsdb }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: opentsdb + type: OpenTsdbWriter + file: features-available/opentsdb.conf + args: "{{ icinga2_dict_features.opentsdb }}" diff --git a/roles/icinga2/tasks/features/perfdata.yml b/roles/icinga2/tasks/features/perfdata.yml index 75ad2b7a..5972f352 100644 --- a/roles/icinga2/tasks/features/perfdata.yml +++ b/roles/icinga2/tasks/features/perfdata.yml @@ -1,12 +1,11 @@ --- - name: feature perfdata PerfdataWriter object - icinga2_object: - name: perfdata - type: PerfdataWriter - file: features-available/perfdata.conf - args: "{{ icinga2_dict_features.perfdata }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: perfdata + type: PerfdataWriter + file: features-available/perfdata.conf + args: "{{ icinga2_dict_features.perfdata }}" diff --git a/roles/icinga2/tasks/features/syslog.yml b/roles/icinga2/tasks/features/syslog.yml index 5c740e7f..cfae48fe 100644 --- a/roles/icinga2/tasks/features/syslog.yml +++ b/roles/icinga2/tasks/features/syslog.yml @@ -1,12 +1,12 @@ --- - name: feature syslog SyslogLogger object - icinga2_object: - name: syslog - type: SyslogLogger - file: features-available/syslog.conf - args: "{{ icinga2_dict_features.syslog }}" - register: result - -- set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [result.dest] }}" + ansible.builtin.set_fact: + icinga2_objects: "{{ icinga2_objects + objects }}" + vars: + objects: + - name: syslog + type: SyslogLogger + file: features-available/syslog.conf + args: "{{ icinga2_dict_features.syslog }}" + register: result diff --git a/roles/icinga2/tasks/objects.yml b/roles/icinga2/tasks/objects.yml index 0c3f720c..9409bac8 100644 --- a/roles/icinga2/tasks/objects.yml +++ b/roles/icinga2/tasks/objects.yml @@ -23,37 +23,39 @@ - icinga2_objects is not string - icinga2_objects is not mapping +- name: Remove duplicate objects from list + ansible.builtin.set_fact: + tmp_objects: "{{ (tmp_objects | default([])) | unique }}" + - icinga2_object: - args: "{{ item }}" - with_items: "{{ tmp_objects }}" - loop_control: - label: "{{ item.type }} '{{ item.name }}'" + objects: "{{ tmp_objects }}" when: tmp_objects is defined register: result - set_fact: - icinga2_local_objects: "{{ icinga2_local_objects|default([]) + [item.dest] }}" - with_items: "{{ result.results }}" - loop_control: - label: "{{ item.dest }}" - when: result.results is defined + icinga2_local_objects: "{{ icinga2_local_objects | default([]) + result.destinations }}" + when: result.destinations is defined - name: prepare custom config when: icinga2_custom_config is defined and icinga2_custom_config|length > 0 block: - name: construct _icinga2_custom_conf_paths set_fact: - _icinga2_custom_conf_paths: "{{ _icinga2_custom_conf_paths + [ icinga2_fragments_path + '/' + item.path + '/' + item.order|default('20')|string + '_' + (item.name | replace('/', '_'))] }}" + _icinga2_custom_conf_paths: "{{ _icinga2_custom_conf_paths + [[icinga2_fragments_path, item.path] | path_join] }}" loop: "{{ icinga2_custom_config }}" - name: prepare custom config paths + loop: "{{ icinga2_custom_config }}" + loop_control: + label: "Creating '{{ _directory_path }}'" + vars: + _directory_path: "{{ [icinga2_fragments_path, item.path] | path_join | dirname }}" file: state: directory owner: root group: root mode: 0755 - path: "{{ icinga2_fragments_path }}/{{ item.path }}/" - loop: "{{ icinga2_custom_config }}" + path: "{{ _directory_path }}" - name: add custom config to assemble ansible.builtin.copy: @@ -61,5 +63,5 @@ group: root mode: 0644 src: "files/{{ item.name }}" - dest: "{{ icinga2_fragments_path }}/{{ item.path }}/{{ item.order|default('20')|string }}_{{ item.name | replace('/', '_') }}" + dest: "{{ [icinga2_fragments_path, item.path] | path_join }}" loop: "{{ icinga2_custom_config }}"