Skip to content

Commit 5adf067

Browse files
Merge pull request #1089 from Checkmk/feature/make-discovery-26-ready
Make the `discovery` module ready for Checkmk 3.0
2 parents b7551ba + d647383 commit 5adf067

4 files changed

Lines changed: 274 additions & 12 deletions

File tree

changelogs/fragments/discovery.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
minor_changes:
2+
- Discovery module - Add support for Checkmk 3.0.
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8; py-indent-offset: 4 -*-
3+
4+
# Copyright: (c) 2022 - 2025,
5+
# Michael Sekania &
6+
# Robin Gierse <robin.gierse@checkmk.com> &
7+
# Max Sickora <max.sickora@checkmk.com> &
8+
# Lars Getwan <lars.getwan@checkmk.com>
9+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
10+
11+
from __future__ import absolute_import, division, print_function
12+
13+
__metaclass__ = type
14+
15+
import json
16+
import time
17+
18+
from ansible_collections.checkmk.general.plugins.module_utils.api import CheckmkAPI
19+
from ansible_collections.checkmk.general.plugins.module_utils.discovery import (
20+
HTTP_CODES,
21+
HTTP_CODES_BULK,
22+
HTTP_CODES_BULK_SC,
23+
HTTP_CODES_SC,
24+
Discovery,
25+
)
26+
from ansible_collections.checkmk.general.plugins.module_utils.types import (
27+
generate_result,
28+
)
29+
30+
COMPATIBLE_MODES = [
31+
"new",
32+
"remove",
33+
"fix_all",
34+
"only_host_labels",
35+
"only_service_labels",
36+
"tabula_rasa",
37+
"refresh",
38+
"monitor_undecided_services",
39+
]
40+
41+
MONITOR_UNDECIDED_SERVICES = [
42+
"new",
43+
"fix_all",
44+
"refresh",
45+
"tabula_rasa",
46+
"monitor_undecided_services",
47+
]
48+
49+
REMOVE_VANISHED_SERVICES = ["remove", "fix_all", "refresh", "tabula_rasa"]
50+
51+
UPDATE_SERVICE_LABELS = [
52+
"only_service_labels",
53+
"fix_all",
54+
"refresh",
55+
"tabula_rasa",
56+
]
57+
58+
UPDATE_HOST_LABELS = [
59+
"new",
60+
"fix_all",
61+
"only_host_labels",
62+
"refresh",
63+
"tabula_rasa",
64+
]
65+
66+
SUPPORTED_VERSIONS = {
67+
"min": "2.6.0",
68+
"max": "3.0.0p99",
69+
}
70+
71+
72+
class ServiceDiscoveryAPI(CheckmkAPI):
73+
def post(self):
74+
mode = self.params.get("state")
75+
if mode not in COMPATIBLE_MODES:
76+
return generate_result(
77+
msg="State %s is not supported with this Checkmk version." % mode
78+
)
79+
80+
if mode == "monitor_undecided_services":
81+
return generate_result(
82+
msg="State %s is only supported in bulk mode." % mode
83+
)
84+
85+
data = {
86+
"host_name": self.params.get("host_name"),
87+
"mode": mode,
88+
}
89+
90+
return self._fetch(
91+
code_mapping=HTTP_CODES,
92+
endpoint="domain-types/service_discovery_run/actions/start/invoke",
93+
data=data,
94+
method="POST",
95+
logger=self.logger,
96+
)
97+
98+
99+
class ServiceBulkDiscoveryAPI(CheckmkAPI):
100+
def post(self):
101+
mode = self.params.get("state")
102+
if mode not in COMPATIBLE_MODES:
103+
return generate_result(
104+
msg="State %s is not supported with this Checkmk version." % mode
105+
)
106+
107+
options = {
108+
"monitor_undecided_services": False,
109+
"remove_vanished_services": False,
110+
"update_service_labels": False,
111+
"update_host_labels": False,
112+
}
113+
114+
if self.params.get("state") in MONITOR_UNDECIDED_SERVICES:
115+
options["monitor_undecided_services"] = True
116+
117+
if self.params.get("state") in REMOVE_VANISHED_SERVICES:
118+
options["remove_vanished_services"] = True
119+
120+
if self.params.get("state") in UPDATE_SERVICE_LABELS:
121+
options["update_service_labels"] = True
122+
123+
if self.params.get("state") in UPDATE_HOST_LABELS:
124+
options["update_host_labels"] = True
125+
126+
data = {
127+
"hostnames": self.params.get("hosts", []),
128+
"options": options,
129+
"do_full_scan": self.params.get("do_full_scan", True),
130+
"bulk_size": self.params.get("bulk_size", 1),
131+
"ignore_errors": self.params.get("ignore_errors", True),
132+
}
133+
134+
return self._fetch(
135+
code_mapping=HTTP_CODES_BULK,
136+
endpoint="domain-types/discovery_run/actions/bulk-discovery-start/invoke",
137+
data=data,
138+
method="POST",
139+
)
140+
141+
142+
class ServiceCompletionAPI(CheckmkAPI):
143+
def get(self):
144+
data = {}
145+
146+
return self._fetch(
147+
code_mapping=HTTP_CODES_SC,
148+
endpoint=(
149+
"objects/service_discovery_run/"
150+
+ self.params.get("host_name")
151+
+ "/actions/wait-for-completion/invoke"
152+
),
153+
data=data,
154+
method="GET",
155+
)
156+
157+
158+
class ServiceCompletionBulkAPI(CheckmkAPI):
159+
def get(self, job_id):
160+
data = {}
161+
162+
return self._fetch(
163+
code_mapping=HTTP_CODES_BULK_SC,
164+
endpoint=("objects/background_job/%s" % job_id),
165+
data=data,
166+
method="GET",
167+
)
168+
169+
170+
class Discovery300(Discovery):
171+
def __init__(self, module, logger):
172+
super().__init__(module, logger)
173+
174+
self.discovery_single = ServiceDiscoveryAPI(self.module, self.logger)
175+
self.discovery_bulk = ServiceBulkDiscoveryAPI(self.module, self.logger)
176+
self.completion_single = ServiceCompletionAPI(self.module, self.logger)
177+
self.completion_bulk = ServiceCompletionBulkAPI(self.module, self.logger)
178+
179+
self.discovery_api = self._discovery_api()
180+
self.service_completion_api = self._service_completion_api()
181+
182+
self.supported_versions = SUPPORTED_VERSIONS
183+
184+
def _discovery_api(self):
185+
if self.single_mode:
186+
return self.discovery_single
187+
188+
return self.discovery_bulk
189+
190+
def _service_completion_api(self):
191+
if self.single_mode:
192+
return self.completion_single
193+
194+
return self.completion_bulk
195+
196+
def _wait_for_completion(self, what, job_id=None):
197+
now = time.time()
198+
199+
if self.timeout > 0:
200+
deadline = now + self.timeout
201+
else:
202+
deadline = 0 # In case of infinite timeout
203+
204+
if self.bulk_mode and not job_id:
205+
return generate_result(
206+
msg=(
207+
"Unable to get job_id for bulk service discovery. "
208+
"Will not wait for job to finish"
209+
),
210+
failed=False,
211+
changed=True,
212+
)
213+
214+
while True:
215+
now = time.time()
216+
if self.timeout > 0 and now > deadline:
217+
return generate_result(
218+
msg="Timeout reached while waiting for %s discovery" % what
219+
)
220+
221+
if self.single_mode:
222+
result = self.service_completion_api.get()
223+
# For single mode, there's a forwarding, but _fetch() doesn't support that.
224+
if result.http_code != 302:
225+
break
226+
227+
else:
228+
# For bulk mode, the completion api shows the state of the job
229+
result = self.service_completion_api.get(job_id)
230+
if not json.loads(result.content).get("extensions").get("active"):
231+
break
232+
233+
time.sleep(3)
234+
235+
return result
236+
237+
def start_discovery(self):
238+
if self.wait_for_previous and self.single_mode:
239+
# For bulk mode, it's neither possible nor necessary to wait for the previous
240+
# discovery, as jobs are now allowed to run in parallel
241+
result = self._wait_for_completion("previous")
242+
if result.failed:
243+
return result
244+
245+
result = self.discovery_api.post()
246+
if result.failed:
247+
return result
248+
249+
if self.wait_for_completion:
250+
if self.single_mode and result.http_code != 200:
251+
result = self._wait_for_completion("current")
252+
elif self.bulk_mode:
253+
job_id = json.loads(result.content).get("id")
254+
result = self._wait_for_completion("current", job_id=job_id)
255+
256+
return result

plugins/modules/discovery.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@
275275
from ansible_collections.checkmk.general.plugins.module_utils.discovery_250 import (
276276
Discovery250,
277277
)
278+
from ansible_collections.checkmk.general.plugins.module_utils.discovery_300 import (
279+
Discovery300,
280+
)
278281
from ansible_collections.checkmk.general.plugins.module_utils.logger import Logger
279282
from ansible_collections.checkmk.general.plugins.module_utils.types import (
280283
generate_result,
@@ -288,6 +291,7 @@
288291

289292
AVAILABLE_API_VERSIONS = [
290293
# Let's try the newest Version, first.
294+
Discovery300,
291295
Discovery250,
292296
Discovery240,
293297
Discovery230,

tests/integration/targets/discovery/tasks/test.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
loop: "{{ checkmk_var_hosts }}"
7171
register: __checkmk_var_result_labels
7272
failed_when: "'State only_service_labels is not supported with this Checkmk version' not in __checkmk_var_result_labels.msg"
73-
when: "not '2.3.0' in outer_item.version and not '2.4.0' in outer_item.version and not '2.5.0' in outer_item.version"
73+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '<')"
7474

7575
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Monitor undecided services. (Should fail < 2.3.0)"
7676
discovery:
@@ -83,7 +83,7 @@
8383
loop: "{{ checkmk_var_hosts }}"
8484
register: __checkmk_var_result_undecided_pre_23
8585
failed_when: "'State monitor_undecided_services is not supported with this Checkmk version' not in __checkmk_var_result_undecided_pre_23.msg"
86-
when: "not '2.3.0' in outer_item.version and not '2.4.0' in outer_item.version and not '2.5.0' in outer_item.version"
86+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '<')"
8787

8888
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Update service labels."
8989
discovery:
@@ -94,7 +94,7 @@
9494
host_name: "{{ item.name }}"
9595
state: "only_service_labels"
9696
loop: "{{ checkmk_var_hosts }}"
97-
when: "'2.3.0' in outer_item.version or '2.4.0' in outer_item.version"
97+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '>=')"
9898

9999
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Monitor undecided services. (Should fail in 2.3.0 and above)"
100100
discovery:
@@ -107,7 +107,7 @@
107107
loop: "{{ checkmk_var_hosts }}"
108108
register: __checkmk_var_result_undecided_post_23
109109
failed_when: "'State monitor_undecided_services is only supported in bulk mode.' not in __checkmk_var_result_undecided_post_23.msg"
110-
when: "'2.3.0' in outer_item.version or '2.4.0' in outer_item.version"
110+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '>=')"
111111

112112
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Tabula Rasa. (New since 2.2)"
113113
discovery:
@@ -118,7 +118,7 @@
118118
host_name: "{{ item.name }}"
119119
state: "tabula_rasa"
120120
loop: "{{ checkmk_var_hosts }}"
121-
when: "not '2.1' in outer_item.version"
121+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.2.0', '>=')"
122122

123123
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Discover hosts (fix_all)."
124124
discovery:
@@ -161,7 +161,7 @@
161161
- "{{ outer_item.site }}"
162162

163163
- name: "Run Bulk Discoveries."
164-
when: "not '2.0.0' in outer_item.version"
164+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.1.0', '>=')"
165165
block:
166166
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Create hosts."
167167
host:
@@ -218,7 +218,7 @@
218218
bulk_size: 5
219219
register: __checkmk_var_result_labels_pre_23
220220
failed_when: "'State only_service_labels is not supported with this Checkmk version.' not in __checkmk_var_result_labels_pre_23.msg"
221-
when: "not '2.3.0' in outer_item.version and not '2.4.0' in outer_item.version and not '2.5.0' in outer_item.version"
221+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '<')"
222222

223223
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Monitor undecided services. (Should fail < 2.3.0)"
224224
discovery:
@@ -231,9 +231,9 @@
231231
bulk_size: 5
232232
register: __checkmk_var_result_labels_post_23
233233
failed_when: "'State monitor_undecided_services is not supported with this Checkmk version.' not in __checkmk_var_result_labels_post_23.msg"
234-
when: "not '2.3.0' in outer_item.version and not '2.4.0' in outer_item.version and not '2.5.0' in outer_item.version"
234+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '<')"
235235

236-
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Update service labels. (only 2.3)"
236+
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Update service labels. (2.3+)"
237237
discovery:
238238
server_url: "{{ checkmk_var_server_url }}"
239239
site: "{{ outer_item.site }}"
@@ -242,9 +242,9 @@
242242
hosts: "{{ checkmk_var_host_names }}"
243243
state: "only_service_labels"
244244
bulk_size: 5
245-
when: "'2.3.0' in outer_item.version or '2.4.0' in outer_item.version"
245+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '>=')"
246246

247-
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Monitor undecided services. (only 2.3)"
247+
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Monitor undecided services. (2.3+)"
248248
discovery:
249249
server_url: "{{ checkmk_var_server_url }}"
250250
site: "{{ outer_item.site }}"
@@ -253,7 +253,7 @@
253253
hosts: "{{ checkmk_var_host_names }}"
254254
state: "monitor_undecided_services"
255255
bulk_size: 5
256-
when: "'2.3.0' in outer_item.version or '2.4.0' in outer_item.version"
256+
when: "(outer_item.version | regex_replace('p.*', '')) is version('2.3.0', '>=')"
257257

258258
- name: "{{ outer_item.version }}.{{ outer_item.edition }} - Bulk: Add undecided services to monitoring."
259259
discovery:

0 commit comments

Comments
 (0)