Hi all,
I just wanted to share my experiences regarding having/getting agent-deployments done in the Ansible-way.
But i wanted to do this without having the requirement/need to import the Ansible checkmk class, just a/the basic Ansible modules at my disposal.
Some details:
- Ansible controller is running off a RockyLinux 9.6 machine.
- Ansible version/installtion was pulled from EPEL-repository ( ansible v 2.14.18)
This worked fine with the playbook i devised previously:
---
- hosts: all
vars:
monitor_host: 'monitor.myhost.tld'
monitor_site: 'mysite'
download_path: '/usr/local/install/packages'
plugin_path: '/usr/lib/check_mk_agent/plugins'
tasks:
- name: Query monitoring host for latest CheckMK version
ansible.builtin.shell: omd versions
register: cmk_versions
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Set latest version of CheckMK package as fact
ansible.builtin.set_fact:
cmk_latest: "{{ cmk_versions.stdout_lines | community.general.version_sort | last | regex_search('[0-9]+.[0-9]+.[0-9]+[a-z][0-9]+') }}"
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Find latest rpm agents on monitoring host
ansible.builtin.find:
path: "/opt/omd/versions/{{ cmk_latest }}.cre/share/check_mk/agents"
patterns: '*.rpm'
register: rpm_agent_result
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Find latest deb agents on monitoring host
ansible.builtin.find:
path: "/opt/omd/versions/{{ cmk_latest }}.cre/share/check_mk/agents"
patterns: '*.deb'
register: deb_agent_result
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Set rpm agent filename as fact
ansible.builtin.set_fact:
rpm_latest: "{{ rpm_agent_result.files[0].path | basename }}"
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Propogate rpm fact to applicable hosts
ansible.builtin.set_fact:
rpm_basename: "{{ hostvars[monitor_host]['rpm_latest'] }}"
when:
- ansible_pkg_mgr == "yum" or ansible_pkg_mgr == "dnf" or ansible_pkg_mgr == "zypper"
- name: Set fact for rpm agent
ansible.builtin.set_fact:
rpm_download: "https://{{ hostvars[monitor_host]['ansible_fqdn'] }}/{{ monitor_site }}/check_mk/agents/{{ hostvars[monitor_host]['rpm_latest'] }}"
when:
- ansible_pkg_mgr == "yum" or ansible_pkg_mgr == "dnf" or ansible_pkg_mgr == "zypper"
- name: Set deb agent filename as fact
ansible.builtin.set_fact:
deb_latest: "{{ deb_agent_result.files[0].path | basename }}"
delegate_to: "{{ monitor_host }}"
run_once: true
- name: Propogate deb fact to applicable hosts
ansible.builtin.set_fact:
deb_basename: "{{ hostvars[monitor_host]['deb_latest'] }}"
when:
- ansible_pkg_mgr == "apt"
- name: Set fact for deb agent
ansible.builtin.set_fact:
deb_download: "https://{{ hostvars[monitor_host]['ansible_fqdn'] }}/{{ monitor_site }}/check_mk/agents/{{ hostvars[monitor_host]['deb_latest'] }}"
when:
- ansible_pkg_mgr == "apt"
- name: Cleanup packages directory before installing (if it exists)
ansible.builtin.file:
path: "{{ download_path }}"
state: absent
- name: Create default directories for installing
ansible.builtin.file:
path: "{{ download_path }}"
state: directory
owner: root
group: root
mode: 0775
- name: Handle Agent download and install of found rpm packages
block:
- name: Download found rpm agent with version from monitoring host
nsible.builtin.get_url:
url: "{{ rpm_download }}"
dest: "{{ download_path }}/{{ rpm_basename }}"
- name: Install rpm agent via YUM
ansible.builtin.yum:
name: "{{ download_path }}/{{ rpm_basename }}"
allow_downgrade: true
state: present
disable_gpg_check: yes
when:
- ansible_pkg_mgr == "yum"
- name: Install rpm agent via ZYPPER
community.general.zypper:
name: "{{ download_path }}/{{ rpm_basename }}"
force: true
state: present
disable_gpg_check: yes
environment:
ZYPP_LOCK_TIMEOUT: 30
when:
- ansible_pkg_mgr == "zypper"
- name: Install rpm agent via DNF
ansible.builtin.dnf:
name: "{{ download_path }}/{{ rpm_basename }}"
allow_downgrade: true
state: present
disable_gpg_check: yes
when:
- ansible_pkg_mgr == "dnf"
when:
- ansible_pkg_mgr == "yum" or ansible_pkg_mgr == "dnf" or ansible_pkg_mgr == "zypper"
- name: Download found deb agent with version from monitoring host
ansible.builtin.get_url:
url: "{{ deb_download }}"
dest: "{{ download_path }}/{{ deb_basename }}"
when:
- ansible_pkg_mgr == "apt"
- name: Install deb Agent package
ansible.builtin.apt:
deb: "{{ download_path }}/{{ deb_basename }}"
allow_downgrade: yes
become: yes
when:
- ansible_pkg_mgr == "apt"
- name: find installed plugins in main plugins directory
ansible.builtin.find:
paths: "{{ plugin_path }}"
file_type: file
recurse: yes
register: plugins_main
- name: Set paths of found plugins as fact
ansible.builtin.set_fact:
plugins_main_found: "{{ plugins_main | json_query('files[*].path') }}"
- name: Update found plugins with versions from monitoring host
ansible.builtin.get_url:
url: "https://{{ hostvars[monitor_host]['ansible_fqdn'] }}/{{ monitor_site }}/check_mk/agents/plugins/{{ item | basename }}"
dest: "{{ item }}"
force: true
with_items:
- "{{ plugins_main_found }}"
ignore_errors: true
- name: Check presense of mk_apt
ansible.builtin.stat:
path: /usr/lib/check_mk_agent/plugins/3600/mk_apt
register: apt_plugin
- name: update setting in mk_apt script to use dist-upgrade
ansible.builtin.lineinfile:
path: /usr/lib/check_mk_agent/plugins/3600/mk_apt
regexp: '^UPGRADE=.*'
line: UPGRADE=dist-upgrade
when:
- apt_plugin.stat.exists
- name: Disable Unwanted CheckMK Services
ansible.builtin.systemd:
state: stopped
enabled: no
name: cmk-agent-ctl-daemon
The above worked fine up until i had (debian-flavoured) hosts started complaining after updates:
An unknown error occurred: HTTPSConnection.__init__() got an unexpected keyword argument 'cert_file'
A bit of research revealed that this has to do with a/the version of Ansible distributed by default in RockyLinux by default.
So to try and avoid/coap this issue i started to look into grabbing the agent over the API on Ansible and created a new playbook for it:
---
- hosts: all
vars:
monitor_host: 'monitor.myhost.tld'
monitor_site: 'mysite'
download_path: '/usr/local/install/packages'
plugin_path: '/usr/lib/check_mk_agent/plugins'
api_user: 'your_automation_user'
api_password: 'your_automation_user_password'
tasks:
- name: Cleanup packages directory before installing (if it exists)
ansible.builtin.file:
path: "{{ download_path }}"
state: absent
- name: Create default directories for installing
ansible.builtin.file:
path: "{{ download_path }}"
state: directory
owner: root
group: root
mode: 0775
- name: Handle CMK Agent download via API and install of found rpm packages
block:
- name: Query rpm agent information via API
ansible.builtin.uri:
url: "https://{{ monitor_host }}/{{ monitor_site }}/check_mk/api/1.0/domain-types/agent/actions/download/invoke?os_type=linux_rpm"
validate_certs: false
headers:
Content-Type: "application/json"
Accept: "*/*"
Authorization: "Bearer {{ api_user }} {{ api_password }}"
method: GET
return_content: true
register: rpm_response
- name: Save .rpm CMK Agent to file
ansible.builtin.copy:
content: "{{ rpm_response.content }}"
dest: "{{ download_path }}/{{ rpm_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
- name: Install rpm agent via YUM
ansible.builtin.yum:
name: "{{ download_path }}/{{ rpm_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
allow_downgrade: true
state: present
disable_gpg_check: yes
when:
- ansible_pkg_mgr == "yum"
- name: Install rpm agent via ZYPPER
community.general.zypper:
name: "{{ download_path }}/{{ rpm_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
force: true
state: present
disable_gpg_check: yes
environment:
ZYPP_LOCK_TIMEOUT: 30
when:
- ansible_pkg_mgr == "zypper"
- name: Install rpm agent via DNF
ansible.builtin.dnf:
name: "{{ download_path }}/{{ rpm_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
allow_downgrade: true
state: present
disable_gpg_check: yes
when:
- ansible_pkg_mgr == "dnf"
when:
- ansible_pkg_mgr == "yum" or ansible_pkg_mgr == "dnf" or ansible_pkg_mgr == "zypper"
- name: Handle CMK Agent download via API and install of found deb packages
block:
- name: Query deb agent information via API
ansible.builtin.uri:
url: "https://{{ monitor_host }}/{{ monitor_site }}/check_mk/api/1.0/domain-types/agent/actions/download/invoke?os_type=linux_deb"
validate_certs: false
headers:
Content-Type: "application/json"
Accept: "*/*"
Authorization: "Bearer {{ api_user }} {{ api_password }}"
method: GET
return_content: true
register: deb_response
- name: Save .deb CMK Agent to file
ansible.builtin.copy:
content: "{{ deb_response.content }}"
dest: "{{ download_path }}/{{ deb_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
- name: Install deb Agent package
ansible.builtin.apt:
deb: "{{ download_path }}/{{ deb_response.content_disposition | regex_findall('filename=\"([^\"]+)\"') | first }}"
allow_downgrade: yes
become: yes
when:
- ansible_pkg_mgr == "apt"
- name: find installed plugins in main plugins directory
ansible.builtin.find:
paths: "{{ plugin_path }}"
file_type: file
recurse: yes
register: plugins_main
- name: Set paths of found plugins as fact
ansible.builtin.set_fact:
plugins_main_found: "{{ plugins_main | json_query('files[*].path') }}"
- name: Update found plugins with versions from monitoring host
ansible.builtin.get_url:
url: "https://{{ hostvars[monitor_host]['ansible_fqdn'] }}/{{ monitor_site }}/check_mk/agents/plugins/{{ item | basename }}"
dest: "{{ item }}"
force: true
with_items:
- "{{ plugins_main_found }}"
ignore_errors: true
- name: Check presense of mk_apt
ansible.builtin.stat:
path: /usr/lib/check_mk_agent/plugins/3600/mk_apt
register: apt_plugin
- name: update setting in mk_apt script to use dist-upgrade
ansible.builtin.lineinfile:
path: /usr/lib/check_mk_agent/plugins/3600/mk_apt
regexp: '^UPGRADE=.*'
line: UPGRADE=dist-upgrade
when:
- apt_plugin.stat.exists
- name: Disable Unwanted CheckMK Services
ansible.builtin.systemd:
state: stopped
enabled: no
name: cmk-agent-ctl-daemon
The way of grabbing the agent this way works, but when you have a few legacy systems they are going to complain ( example is from SLES12.3 systems) next to the above issue with i was trying to get rid of.
fatal: [sles12.3.myhost.tld]: FAILED! => {"changed": false, "msg": "could not write content temp file: 'utf-8' codec can't encode character '\\uda5b' in position 1257: surrogates not allowed"}
So in changing the way i was trying to grab the Agent, instead of making it work i now introduced another issue.
Then i dug into the original error, and there seems to be an issue with the Ansible version being supplied via EPEL / RockyLinux.
However there was no newer version available, or i would have to go the ‘pip’-way of deploying a newer version.
Pip however is annoying as it is user-specific.
So instead ( after watching a video https://www.youtube.com/watch?v=9eHtelvVi6o, the alternative was to install a/the newer version via GIT.
For me this worked perfectly, as after that i was able to deploy my agents again ( apart from the SLES12.3 -hosts) with a notice tho:
[DEPRECATION WARNING]: Module "ansible.builtin.uri" returned non UTF-8 data in the JSON response. This will become an error in the future. This feature will
be removed in version 2.18. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
So yes it works, but in retrospect, the api-method ( most likely can be refined) is slower, and breaks legacy systems.
So the way to go is imho update your Ansible version (to atleast 2.15.x) via GIT, and then use the initial method of deploying your agents, not triggering depreciation -stuff/messages in Ansible, and not breaking legacy host-compatibillity.
- Glowsome