diff --git a/agents/bakery/curl.py b/agents/bakery/curl.py index c28137e7dd4c20fe4c786afa6c1aa6633c2e761a..28169f8d6a882c286314b7dd0f905ef508e40ea9 100755 --- a/agents/bakery/curl.py +++ b/agents/bakery/curl.py @@ -48,10 +48,15 @@ # added options --path-as-is, --ssl-allow-beast, --no-buffer, --no-keepalive, --no-sessionid # 2022-03-28: added option --crlf # added SMTP settings: --mail-auth, --mail-from, --mail-rcpt, --mail-rcpt-allowfails, --upload-file (SMTP) +# 2022-04-10: added deployment of cURL executables +# no separate WATO rules per OS necessary anymore +# reworked to make scalable for multiple OSs (THX to andreas.doehler[at]gmail[dot]com) +# removed data adjustments between default and per url settings after WATO rework # from pathlib import Path -from typing import List, Tuple, Dict +from typing import List, Dict, Any +from dataclasses import dataclass from cmk.utils import ( password_store, @@ -65,53 +70,69 @@ from cmk.base.cee.plugins.bakery.bakery_api.v1 import ( ) -bakery_version = '20220325.v0.0.6' - - -def get_curl_files(conf: Tuple[str, Dict[str, List[any]]]) -> FileGenerator: - if conf[0] == 'linux': - _os = OS.LINUX - _script = 'curl.sh' - _curl_output = '--output /dev/null' - _temp_path = '/var/tmp/' - _conf_path = '/etc/check_mk/' - elif conf[0] == 'windows': - _os = OS.WINDOWS - _script = 'curl.ps1' - _curl_output = '--output NUL' - _temp_path = 'c:/windows/temp/' - _conf_path = 'C:/ProgramData/checkmk/agent/config/' - else: - _os = None - _script = None - _curl_output = None - _temp_path = None - _conf_path = None - +@dataclass +class CurlConfig: + base_os: OS + curl_output: str + temp_path: str + conf_path: str + traget_path_bin: str + plugin_name: str + curl_executable_src: Dict[str, str] + curl_executable_dest: str + + +CURL_CONFIGS: List[CurlConfig] = [ + CurlConfig( + base_os=OS.LINUX, + curl_output="--output /dev/null", + temp_path="/var/tmp/", + conf_path="/etc/check_mk/", + traget_path_bin='../bin/curl', + plugin_name='curl.sh', + curl_executable_src={'64bit': 'curl-amd64', '32bit': 'curl-i386'}, + curl_executable_dest='../bin/curl', + ), + CurlConfig( + base_os=OS.WINDOWS, + curl_output="--output NUL", + temp_path="c:/windows/temp/", + conf_path="C:/ProgramData/checkmk/agent/config/", + traget_path_bin='..\\bin\\curl.exe', + plugin_name='curl.ps1', + curl_executable_src={'64bit': 'curl.exe.64', '32bit': 'curl.exe.32'}, + curl_executable_dest='..\\bin\\curl.exe', + ), +] + +bakery_version = '20220410.v0.0.7' + + +def get_curl_files(conf) -> FileGenerator: field_separator: str = '|' # needs matching separator in the shell scripts - options_separator: str = ' ' - options = conf[1].copy() - url_cfg_lines = [] + # catch pre 20220410 WATO format + options: Dict[str, Any] = conf[1].copy() if type(conf) == tuple else conf + + url_cfg_lines = [] url_list = options['url_list'] default_settings = options.get('default_settings', {}) - interval = None - timeout = None - - if options.get('interval'): - interval = options['interval'] * 60 # minutes to seconds - options.pop('interval') + interval = options['interval'] * 60 if options.get('interval') else None + timeout = options['timeout'] * 60 if options.get('timeout') else None - if options.get('timeout'): - timeout = options['timeout'] * 60 # minutes to seconds - options.pop('timeout') + if options.get('curl_executable'): + for curl_config in CURL_CONFIGS: + yield Plugin( + base_os=curl_config.base_os, + source=Path(curl_config.curl_executable_src[str(options['curl_executable'])]), + target=Path(curl_config.curl_executable_dest), + ) + yield Plugin(base_os=curl_config.base_os, source=Path('curl-ca-bundle.crt')) curl_item = 0 for entry in url_list: curl_item += 1 - options_array = [] - header_array = [] regex_option = 'no_regex' save_output = False # get service name and url, first try new format, then old format @@ -121,8 +142,6 @@ def get_curl_files(conf: Tuple[str, Dict[str, List[any]]]) -> FileGenerator: service_name = entry['service_name'] url = entry['url'] - options_array.append(f'--url "{url}"') - url_settings = default_settings.copy() entry = entry.get('url_settings', {}).copy() @@ -150,394 +169,284 @@ def get_curl_files(conf: Tuple[str, Dict[str, List[any]]]) -> FileGenerator: # merge per url settings with default settings url_settings.update(entry) - # adjust per url settings with default settings (dictionary) - for key in [ - 'expected_strings', - 'header_strings', - 'referer', - 'request_headers', - ]: - if type(url_settings.get(key)) == tuple: - url_settings.update({key: url_settings[key][1]}) - - for key in [ - 'limit_rate', - 'max_file_size', - 'max_time', - 'speed_limit', - 'speed_time', - 'connect_timeout', - 'api_key_header', - # 'user_auth', - ]: - if type(url_settings.get(key)) == tuple: - if url_settings[key][0] == key: - url_settings.update({key: url_settings[key][1]}) - # adjust per url settings with default settings (tuple) - for key in [ - 'http_proxy', - ]: - if type(url_settings.get(key)) == tuple: - if type(url_settings[key][1]) == dict: - url_settings.update({key: url_settings[key][1]}) - - # filter options - if url_settings.get('http_proxy'): - if url_settings['http_proxy'] == '--noproxy': - options_array.append("--noproxy '*'") - else: - proxy_protocol, proxy_server, proxy_port = url_settings['http_proxy']['proxy_server'] - options_array.append(f'{proxy_protocol} {proxy_server}:{proxy_port}') - if url_settings['http_proxy'].get('proxy_auth'): - proxy_user, proxy_password, proxy_auth = url_settings['http_proxy']['proxy_auth'] - if proxy_password[0] == 'store': - pw = password_store.extract(proxy_password[1]) - else: - pw = proxy_password[1] - options_array.append(f'--proxy-user {proxy_user}:{pw}') - options_array.append(proxy_auth) - url_settings.pop('http_proxy') - - if url_settings.get('user_auth'): - if url_settings['user_auth'][0] == 'user_auth': - user, user_password, user_auth = url_settings['user_auth'][1] - if user_password[0] == 'store': - pw = password_store.extract(user_password[1]) + for curl_config in CURL_CONFIGS: + _os = curl_config.base_os + _curl_output = curl_config.curl_output + _temp_path = curl_config.temp_path + _conf_path = curl_config.conf_path + + _options = [f'--url "{url}"'] + _headers = [] + + # filter options + _options.append(url_settings['get_header_only']) if url_settings.get('get_header_only') else None + _options.append(url_settings['compressed']) if url_settings.get('compressed') else None + _options.append(f'--max-time {url_settings["max_time"]}') if url_settings.get('max_time') else None + _options.append(f'--speed-time {url_settings["speed_time"]}') if url_settings.get('speed_time') else None + _options.append(f'--connect-timeout {url_settings["connect_timeout"]}') if url_settings.get('connect_timeout') else None + _options.append(f'--user-agent "{url_settings["user_agent"]}"') if url_settings.get('user_agent') else None + _options.append(f'--referer {url_settings["referer"]}') if url_settings.get('referer') else None + _options.append(f'{url_settings["tls_ssl_version"]}') if url_settings.get('tls_ssl_version') else None + _options.append(f'{url_settings["http_version"]}') if url_settings.get('http_version') else None + + if url_settings.get('max_file_size'): + max_size, unit = url_settings['max_file_size'] + _options.append(f'--max-filesize {max_size}{unit}') + + if url_settings.get('speed_limit'): + speed, unit = url_settings['speed_limit'] + _options.append(f'--speed-limit {speed * unit}') + + if url_settings.get('limit_rate'): + speed, unit = url_settings['limit_rate'] + _options.append(f'--limit-rate {speed}{unit}') + + if url_settings.get('http_proxy'): + if url_settings['http_proxy'] == '--noproxy': + _options.append("--noproxy '*'") else: - pw = user_password[1] - options_array.append(f'--user {user}:{pw}') - options_array.append(user_auth) - elif url_settings['user_auth'][0] == 'priv_key_auth': - user, pass_phrase, priv_key = url_settings['user_auth'][1] - options_array.append(f'--user {user}:') - options_array.append(f'--key {_conf_path}curl/curl_item_{curl_item}.priv_key') - if pass_phrase[0] == 'store': - pw = password_store.extract(pass_phrase[1]) + proxy_protocol, proxy_server, proxy_port = url_settings['http_proxy']['proxy_server'] + _options.append(f'{proxy_protocol} {proxy_server}:{proxy_port}') + if url_settings['http_proxy'].get('proxy_auth'): + proxy_user, proxy_password, proxy_auth = url_settings['http_proxy']['proxy_auth'] + if proxy_password[0] == 'store': + pw = password_store.extract(proxy_password[1]) + else: + pw = proxy_password[1] + _options.append(f'--proxy-user {proxy_user}:{pw}') + _options.append(proxy_auth) + + if url_settings.get('request_headers'): + for header in url_settings['request_headers']: + key, value = header + _headers.append(f'{key}:{value}') + + if url_settings.get('api_key_header'): + api_header, api_key = url_settings['api_key_header'] + api_header = api_header.rstrip(':') + if api_key[0] == 'store': + api_key = password_store.extract(api_key[1]) else: - pw = pass_phrase[1] - options_array.append(f'--pass {pw}') + api_key = api_key[1] + _headers.append(f'{api_header}:{api_key}') + + if url_settings.get('redirects'): + location, location_trusted, max_redirects = url_settings['redirects'] + _options.append(f'--location') if location else None + _options.append(f'--location-trusted') if location_trusted else None + _options.append(f'--max-redirs {max_redirects}') if max_redirects else None + + if url_settings.get('advanced_settings'): + allow_beast, cr2lf, no_apln, no_buffering, no_npn, no_sessionid, no_keepalive, \ + path_as_is, tcp_fastopen, tcp_nodelay = url_settings['advanced_settings'] + + _options.append(f'--ssl-allow-beast') if allow_beast else None + _options.append(f'--crlf') if cr2lf else None + _options.append(f'--no-alpn') if no_apln else None + _options.append(f'--no-buffer') if no_buffering else None + _options.append(f'--no-npn') if no_npn else None + _options.append(f'--no-sessionid') if no_sessionid else None + _options.append(f'--no-keepalive') if no_keepalive else None + _options.append(f'--path-as-is') if path_as_is else None + _options.append(f'--tcp-fastopen') if tcp_fastopen else None + _options.append(f'--tcp-nodelay') if tcp_nodelay else None + + if url_settings.get('ip_address_resolution'): + dns_options = url_settings['ip_address_resolution'] + _options.append(dns_options['dns_resolve_names']) if dns_options.get('dns_resolve_names') else None + _options.append(f'--dns-interface {dns_options["dns_source_interface"]}') if dns_options.get('dns_source_interface') else None + _options.append(f'--dns-ipv4-addr {dns_options["dns_source_ipv4"]}') if dns_options.get('dns_source_ipv4') else None + _options.append(f'--dns-ipv6-addr {dns_options["dns_source_ipv6"]}') if dns_options.get('dns_source_ipv6') else None + _options.append(f'--dns-servers {",".join(dns_options["dns_servers"])}') if dns_options.get('dns_servers') else None + + if url_settings.get('ftp_settings'): + ftp_options = url_settings['ftp_settings'] + _options.append(f'--compressed-ssh') if ftp_options.get('compressed_ssh') else None + _options.append(f'--list-only') if ftp_options.get('list_only') else None + _options.append(f'--use-ascii') if ftp_options.get('use_ascii') else None + _options.append(f'--ftp-create-dirs') if ftp_options.get('ftp_create_dirs') else None + _options.append(f'--ftp-account {ftp_options["ftp_account"]}') if ftp_options.get('ftp_account') else None + _options.append(f'--ftp-alternative-to-user {ftp_options["ftp_alternate_to_user"]}') if ftp_options.get('ftp_alternate_to_user') else None + _options.append(f'--ftp-method {ftp_options["ftp_change_cwd_method"]}') if ftp_options.get('ftp_change_cwd_method') else None + + if ftp_options.get('ftp_mode'): + ftp_mode = ftp_options['ftp_mode'] + if ftp_mode[0] == 'ftp_pass': + no_send_epsv, send_pret, skip_ip = ftp_mode[1] + _options.append(f'--ftp-pasv') + _options.append(f'--disable-epsv') if no_send_epsv else None + _options.append(f'--ftp-pret') if send_pret else None + _options.append(f'--ftp-skip-pasv-ip') if skip_ip else None + else: + no_send_eprt, active_address = ftp_options[1] + _options.append(f'--disable-eprt') if no_send_eprt else None + _options.append(f'--ftp-port {active_address}') if active_address else None + if ftp_options.get('ftp_ssl_control'): + _options.append(f'--ftp-ssl-control') + ftp_ssl_ccc, ftp_ssl_ccc_mode = ftp_options['ftp_ssl_control'] + if ftp_ssl_ccc: + _options.append(f'--ftp-ssl-ccc') + _options.append(f'--ftp-ssl-ccc-mode {ftp_ssl_ccc_mode}') + + if url_settings.get('user_auth'): + if url_settings['user_auth'][0] == 'user_auth': + user, user_password, user_auth = url_settings['user_auth'][1] + pw = password_store.extract(user_password[1]) if user_password[0] == 'store' else user_password[1] + _options.append(f'--user {user}:{pw}') + _options.append(user_auth) + elif url_settings['user_auth'][0] == 'priv_key_auth': + user, pass_phrase, priv_key = url_settings['user_auth'][1] + pw = password_store.extract(pass_phrase[1]) if pass_phrase[0] == 'store' else pass_phrase[1] + _options.append(f'--user {user}:') + _options.append(f'--key {_conf_path}curl/curl_item_{curl_item}.priv_key') + _options.append(f'--pass {pw}') + yield PluginConfig( + base_os=_os, + lines=[priv_key], + target=Path(f'curl/curl_item_{curl_item}.priv_key'), + include_header=False, + ) + + if url_settings.get('expected_strings'): + save_output = True yield PluginConfig( base_os=_os, - lines=[priv_key], - target=Path(f'curl/curl_item_{curl_item}.priv_key'), + lines=url_settings['expected_strings'], + target=Path(f'curl/curl_item_{curl_item}.search_response'), include_header=False, ) - url_settings.pop('user_auth') - - if url_settings.get('expected_strings'): - save_output = True - yield PluginConfig( - base_os=_os, - lines=url_settings['expected_strings'], - target=Path(f'curl/curl_item_{curl_item}.search_response'), - include_header=False, - ) - url_settings.pop('expected_strings') - if url_settings.get('header_strings'): - options_array.append(f'--dump-header {_temp_path}curl_header') - yield PluginConfig( - base_os=_os, - lines=url_settings['header_strings'], - target=Path(f'curl/curl_item_{curl_item}.search_header'), - include_header=False, - ) - url_settings.pop('header_strings') - - if url_settings.get('limit_rate'): - speed, unit = url_settings['limit_rate'] - options_array.append(f'--limit-rate {speed}{unit}') - url_settings.pop('limit_rate') - - if url_settings.get('max_file_size'): - max_size, unit = url_settings['max_file_size'] - options_array.append(f'--max-filesize {max_size}{unit}') - url_settings.pop('max_file_size') - - if url_settings.get('max_time'): - options_array.append(f'--max-time {url_settings["max_time"]}') - url_settings.pop('max_time') - - if url_settings.get('speed_limit'): - speed, unit = url_settings['speed_limit'] - options_array.append(f'--speed-limit {speed * unit}') - url_settings.pop('speed_limit') - - if url_settings.get('speed_time'): - options_array.append(f'--speed-time {url_settings["speed_time"]}') - url_settings.pop('speed_time') - - if url_settings.get('connect_timeout'): - options_array.append(f'--connect-timeout {url_settings["connect_timeout"]}') - url_settings.pop('connect_timeout') - - if url_settings.get('user_agent'): - user_agent = url_settings['user_agent'] - options_array.append(f'--user-agent "{user_agent}"') - url_settings.pop('user_agent') - - if url_settings.get('referer'): - referer = url_settings['referer'] - options_array.append(f'--referer {referer}') - url_settings.pop('referer') - - if url_settings.get('request_headers'): - for header in url_settings['request_headers']: - key, value = header - header_array.append(f'{key}:{value}') - # options_array.append(f'--header "{key}:{value}"') - url_settings.pop('request_headers') - - if url_settings.get('api_key_header'): - api_header, api_key = url_settings['api_key_header'] - api_header = api_header.rstrip(':') - if api_key[0] == 'store': - api_key = password_store.extract(api_key[1]) - else: - api_key = api_key[1] - header_array.append(f'{api_header}:{api_key}') - # options_array.append(f'--header "{api_header}:{api_key}"') - url_settings.pop('api_key_header') - - if url_settings.get('redirects'): - location, location_trusted, max_redirects = url_settings['redirects'] - if location: - options_array.append(f'--location') - if location_trusted: - options_array.append(f'--location-trusted') - if max_redirects: - options_array.append(f'--max-redirs {int(max_redirects)}') - url_settings.pop('redirects') - - if url_settings.get('cert_verify'): - pub_md5 = None - pub_sha256 = None - pub_key = None - try: # 2022-03-23: added ssh settings - insecure, ocsp, no_revoke, cert_chain = url_settings['cert_verify'] - except ValueError: # 2022-03-23: added ssh settings - insecure, ocsp, no_revoke, cert_chain, pub_md5, pub_sha256, pub_key = url_settings['cert_verify'] - if insecure: - options_array.append(f'--insecure') - if ocsp: - options_array.append(f'--cert-status') - if no_revoke: - options_array.append(f'--ssl-no-revoke') - if cert_chain: - cert_chain = cert_chain[cert_chain.find('-----BEGIN CERTIFICATE-----'):] - yield PluginConfig( - base_os=_os, - lines=[cert_chain], - target=Path(f'curl/curl_item_{curl_item}.ca_cert'), - include_header=False, - ) - options_array.append(f'--cacert {_conf_path}curl/curl_item_{curl_item}.ca_cert') - if pub_md5: - options_array.append(f'--hostpubmd5 {pub_md5}') - if pub_sha256: - options_array.append(f'--hostpubsha256 {pub_sha256}') - if pub_key: + if url_settings.get('header_strings'): + _options.append(f'--dump-header {_temp_path}curl_header') yield PluginConfig( base_os=_os, - lines=[pub_key], - target=Path(f'curl/curl_item_{curl_item}.pub_key'), + lines=url_settings['header_strings'], + target=Path(f'curl/curl_item_{curl_item}.search_header'), include_header=False, ) - options_array.append(f'--pubkey {_conf_path}curl/curl_item_{curl_item}.pub_key') - - url_settings.pop('cert_verify') - - if url_settings.get('advanced_settings'): - allow_beast, cr2lf, no_apln, no_buffering, no_npn, no_sessionid, no_keepalive, \ - path_as_is, tcp_fastopen, tcp_nodelay = url_settings['advanced_settings'] - if allow_beast: - options_array.append(f'--ssl-allow-beast') - if cr2lf: - options_array.append(f'--crlf') - if no_apln: - options_array.append(f'--no-alpn') - if no_buffering: - options_array.append(f'--no-buffer') - if no_npn: - options_array.append(f'--no-npn') - if no_sessionid: - options_array.append(f'--no-sessionid') - if no_keepalive: - options_array.append(f'--no-keepalive') - if path_as_is: - options_array.append(f'--path-as-is') - if tcp_fastopen: - options_array.append(f'--tcp-fastopen') - if tcp_nodelay: - options_array.append(f'--tcp-nodelay') - url_settings.pop('advanced_settings') - - if url_settings.get('post_binary'): - header_array.append(f'content-type: {url_settings["post_binary"][0]}') - yield PluginConfig( - base_os=_os, - lines=[url_settings["post_binary"][1]], - target=Path(f'curl/curl_item_{curl_item}.post_binary'), - include_header=False, - ) - options_array.append(f'--data-binary @{_conf_path}curl/curl_item_{curl_item}.post_binary') - url_settings.pop('post_binary') - if url_settings.get('regex_response'): - regex_str, no_case, multi_line = url_settings['regex_response'] - if regex_str: - save_output = True + if url_settings.get('cert_verify'): + pub_md5 = None + pub_sha256 = None + pub_key = None + try: # 2022-03-23: added ssh settings + insecure, ocsp, no_revoke, cert_chain = url_settings['cert_verify'] + except ValueError: # 2022-03-23: added ssh settings + insecure, ocsp, no_revoke, cert_chain, pub_md5, pub_sha256, pub_key = url_settings['cert_verify'] + + _options.append(f'--insecure') if insecure else None + _options.append(f'--cert-status') if ocsp else None + _options.append(f'--ssl-no-revoke') if no_revoke else None + _options.append(f'--hostpubmd5 {pub_md5}') if pub_md5 else None + _options.append(f'--hostpubsha256 {pub_sha256}') if pub_sha256 else None + + if cert_chain: + cert_chain = cert_chain[cert_chain.find('-----BEGIN CERTIFICATE-----'):] + yield PluginConfig( + base_os=_os, + lines=[cert_chain], + target=Path(f'curl/curl_item_{curl_item}.ca_cert'), + include_header=False, + ) + _options.append(f'--cacert {_conf_path}curl/curl_item_{curl_item}.ca_cert') + + if pub_key: + yield PluginConfig( + base_os=_os, + lines=[pub_key], + target=Path(f'curl/curl_item_{curl_item}.pub_key'), + include_header=False, + ) + _options.append(f'--pubkey {_conf_path}curl/curl_item_{curl_item}.pub_key') + + if url_settings.get('post_binary'): + _headers.append(f'content-type: {url_settings["post_binary"][0]}') yield PluginConfig( base_os=_os, - lines=[regex_str], - target=Path(f'curl/curl_item_{curl_item}.regex'), + lines=[url_settings["post_binary"][1]], + target=Path(f'curl/curl_item_{curl_item}.post_binary'), include_header=False, ) - if no_case: - regex_option = 'nocase' - else: - regex_option = 'case' - if multi_line: - regex_option += '_multiline' - else: - regex_option += '_nomultiline' - - url_settings.pop('regex_response') - - if url_settings.get('ip_address_resolution'): - dns_options = url_settings['ip_address_resolution'] - if dns_options.get('dns_resolve_names'): - options_array.append(dns_options['dns_resolve_names']) - if dns_options.get('ens_source_interface'): - options_array.append(f'--dns-interface {dns_options["ens_source_interface"]}') - if dns_options.get('dns_source_ipv4'): - options_array.append(f'--dns-ipv4-addr {dns_options["dns_source_ipv4"]}') - if dns_options.get('dns_source_ipv6'): - options_array.append(f'--dns-ipv6-addr {dns_options["dns_source_ipv6"]}') - if dns_options.get('dns_servers'): - options_array.append(f'--dns-servers {",".join(dns_options["dns_servers"])}') - url_settings.pop('ip_address_resolution') - - if url_settings.get('ftp_settings'): - ftp_options = url_settings['ftp_settings'] - if ftp_options.get('ftp_account'): - options_array.append(f'--ftp-account {ftp_options["ftp_account"]}') - if ftp_options.get('ftp_alternate_to_user'): - options_array.append(f'--ftp-alternative-to-user {ftp_options["ftp_alternate_to_user"]}') - if ftp_options.get('ftp_create_dirs'): - options_array.append(f'--ftp-create-dirs') - if ftp_options.get('ftp_change_cwd_method'): - options_array.append(f'--ftp-method {ftp_options["ftp_change_cwd_method"]}') - if ftp_options.get('ftp_mode'): - ftp_mode = ftp_options['ftp_mode'] - if ftp_mode[0] == 'ftp_pass': - options_array.append(f'--ftp-pasv') - no_send_epsv, send_pret, skip_ip = ftp_mode[1] - if no_send_epsv: - options_array.append(f'--disable-epsv') - if send_pret: - options_array.append(f'--ftp-pret') - if skip_ip: - options_array.append(f'--ftp-skip-pasv-ip') - else: - no_send_eprt, active_address = ftp_options[1] - if no_send_eprt: - options_array.append(f'--disable-eprt') - if active_address: - options_array.append(f'--ftp-port {active_address}') - if ftp_options.get('ftp_ssl_control'): - options_array.append(f'--ftp-ssl-control') - ftp_ssl_ccc, ftp_ssl_ccc_mode = ftp_options['ftp_ssl_control'] - if ftp_ssl_ccc: - options_array.append(f'--ftp-ssl-ccc') - options_array.append(f'--ftp-ssl-ccc-mode {ftp_ssl_ccc_mode}') - if ftp_options.get('compressed_ssh'): - options_array.append(f'--compressed-ssh') - if ftp_options.get('list_only'): - options_array.append(f'--list-only') - if ftp_options.get('use_ascii'): - options_array.append(f'--use-ascii') - url_settings.pop('ftp_settings') - - if url_settings.get('mail_settings'): - mail_options = url_settings['mail_settings'] - if mail_options.get('mail_from'): - options_array.append(f'--mail-from {mail_options["mail_from"]}') - if mail_options.get('mail_rcpt'): - for address in mail_options['mail_rcpt']: - options_array.append(f'--mail-rcpt {address}') - if mail_options.get('mail_auth'): - options_array.append(f'--mail-auth {mail_options["mail_auth"]}') - if mail_options.get('request'): - options_array.append(f'--request {mail_options["request"]}') - if mail_options.get('mail_rpct_allowfail'): - options_array.append(f'--mail-rcpt-allowfails') - message = [] - if mail_options.get('request_headers'): - for header, value in mail_options['request_headers']: - message.append(f'{header}: {value}') - if mail_options.get('message'): - message.append(mail_options['message']) - if message: - options_array.append(f'--upload-file {_conf_path}curl/curl_item_{curl_item}.message') + _options.append(f'--data-binary @{_conf_path}curl/curl_item_{curl_item}.post_binary') + + if url_settings.get('regex_response'): + regex_str, no_case, multi_line = url_settings['regex_response'] + if regex_str: + save_output = True + yield PluginConfig( + base_os=_os, + lines=[regex_str], + target=Path(f'curl/curl_item_{curl_item}.regex'), + include_header=False, + ) + regex_option = 'nocase' if no_case else 'case' + regex_option += '_multiline' if multi_line else '_nomultiline' + + if url_settings.get('mail_settings'): + mail_options = url_settings['mail_settings'] + + _options.append(f'--mail-from {mail_options["mail_from"]}') if mail_options.get('mail_from') else None + _options.append(f'--mail-auth {mail_options["mail_auth"]}') if mail_options.get('mail_auth') else None + _options.append(f'--request {mail_options["request"]}') if mail_options.get('request') else None + _options.append(f'--mail-rcpt-allowfails') if mail_options.get('mail_rpct_allowfail') else None + + if mail_options.get('mail_rcpt'): + for address in mail_options['mail_rcpt']: + _options.append(f'--mail-rcpt {address}') + + message = [] + message.append(mail_options['message']) if mail_options.get('message') else None + if mail_options.get('request_headers'): + for header, value in mail_options['request_headers']: + message.append(f'{header}: {value}') + if message: + _options.append(f'--upload-file {_conf_path}curl/curl_item_{curl_item}.message') + yield PluginConfig( + base_os=_os, + lines=message, + target=Path(f'curl/curl_item_{curl_item}.message'), + include_header=False, + ) + + _options.append(f'--output {_temp_path}curl_output') if save_output else _options.append(_curl_output) + + if _headers: yield PluginConfig( base_os=_os, - lines=message, - target=Path(f'curl/curl_item_{curl_item}.message'), + lines=_headers, + target=Path(f'curl/curl_item_{curl_item}.header'), include_header=False, ) - url_settings.pop('mail_settings') + _options.append(f'--header @{_conf_path}curl/curl_item_{curl_item}.header') - if save_output: - options_array.append(f'--output {_temp_path}curl_output') - else: - options_array.append(_curl_output) - - if header_array: yield PluginConfig( base_os=_os, - lines=header_array, - target=Path(f'curl/curl_item_{curl_item}.header'), - include_header=False, + lines=_options, + target=Path(f'curl/curl_item_{curl_item}.options'), + include_header=True, ) - options_array.append(f'--header @{_conf_path}curl/curl_item_{curl_item}.header') - - for value in url_settings.values(): - options_array.append(value) - curl_options = options_separator.join(options_array) - while ' ' in curl_options: - curl_options = curl_options.replace(' ', ' ') url_cfg_lines.append( f'{service_name}{field_separator}' - # f'"{url}"{field_separator}' - # f'{curl_options.strip()}{field_separator}' f'curl_item_{curl_item}{field_separator}' f'{regex_option}{field_separator}' ) + + for curl_config in CURL_CONFIGS: + yield Plugin( + base_os=curl_config.base_os, + source=Path(curl_config.plugin_name), + interval=interval, + timeout=timeout + ) yield PluginConfig( - base_os=_os, - lines=options_array, - target=Path(f'curl/curl_item_{curl_item}.options'), - include_header=True, + base_os=curl_config.base_os, + lines=url_cfg_lines, + target=Path('curl.cfg'), + include_header=False ) - yield Plugin( - base_os=_os, - source=Path(_script), - target=Path(_script), - interval=interval, - timeout=timeout, - ) - - yield PluginConfig( - base_os=_os, - lines=url_cfg_lines, - target=Path('curl.cfg'), - include_header=False, - ) - register.bakery_plugin( name='curl', diff --git a/curl.mkp b/curl.mkp index ecf9c70ffb7ca71c57ec484f831358704877964a..c6feb860a25fcd019e4f88f5cb52c76ce6417b68 100644 Binary files a/curl.mkp and b/curl.mkp differ diff --git a/packages/curl b/packages/curl index 1a8e612fd88c6dabfa5c5baf870df88f54d6402f..a769adf20929392d244c5d113a80e658b226256c 100644 --- a/packages/curl +++ b/packages/curl @@ -2,18 +2,21 @@ 'description': 'cURL agent plugin\n' '\n' 'Monitor URLs from the perspective of your monitored hosts, ' - 'not the monitoring server ;-). Deployment and configuration ' - 'of the plugin is integrated in the CMK bakery.\n' + 'not the monitoring server ;-). \n' + '\n' + 'The plugin is based on a idea by Christian Wirtz ' + 'doc[at]snowheaven[dot]de and Ingo Hambrock.\n' '\n' - 'The plugin is based on a idea by based on the work by ' - 'Christian Wirtz doc[at]snowheaven[dot]de and Ingo Hambrock\n' 'This agent plugin for Linux and Windows is build around the ' 'curl command line tool from https://curl.se.\n' '\n' 'The executable is not included in this package. The plugin ' 'will use by default the system provided curl executable.\n' '\n' - 'Note: this plugin needs at leat curl version 7.70.0 from ' + 'Deployment and configuration of the plugin is integrated in ' + 'the CMK bakery.\n' + '\n' + 'Note: this plugin needs at least curl version 7.70.0 from ' 'April 29 2020 to work. You can use my CMK package ' '"curl_executable.mkp" to deploy a compatible version.\n', 'download_url': 'https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/curl', @@ -23,7 +26,7 @@ 'name': 'curl', 'num_files': 6, 'title': 'cURL agent plugin', - 'version': '20220406.v0.1.6', + 'version': '20220410.v0.1.7', 'version.min_required': '2.0.0', 'version.packaged': '2021.09.20', 'version.usable_until': None} \ No newline at end of file diff --git a/web/plugins/wato/curl.py b/web/plugins/wato/curl.py index b051c4b691ffc0c72774484e158ee9a6795aefef..56adc424c780a26f1e1ef5b99a190b58642bd73a 100644 --- a/web/plugins/wato/curl.py +++ b/web/plugins/wato/curl.py @@ -65,6 +65,11 @@ # 2022-03-25: added options --compressed-ssh, --list-only, --use-ascii # added options --path-as-is, --ssl-allow-beast, --no-buffer, --no-keepalive, --no-sessionid # 2022-03-28: added options --mail-auth, --mail-from, --mail-rcpt, --mail-rcpt-allowfails, --crlf, --upload-file (SMTP) +# 2022-04-10: added options to deploy cURL executable, no separate rules for curl and curl executable anymore +# windows/linux summarized under on option, no separate WATO rules per OS necessary anymore +# replaced most CascadingDropdown with Alternative to avoid the need for adusting default and url settings +# in the bakery +# import ipaddress from cmk.gui.i18n import _ @@ -89,6 +94,7 @@ from cmk.gui.valuespec import ( Age, # Url, UploadOrPasteTextFile, + Alternative, ) from cmk.gui.plugins.wato import ( rulespec_registry, @@ -104,7 +110,7 @@ from cmk.gui.cee.plugins.wato.agent_bakery.rulespecs.utils import ( RulespecGroupMonitoringAgentsAgentPlugins, ) -bakery_plugin_version = '20220325.v0.0.6' +bakery_plugin_version = '20220410.v0.0.7' # unsafe characters https://www.tutorialspoint.com/html/html_url_encoding.htm forbidden_chars = '"<>#%{}|\^~[]` \'' @@ -147,7 +153,7 @@ _curl_check_elements = [ )), ('http_error_code_to_ignore', ListOfStrings( - title=_('HTTP:q error codes to ignore'), + title=_('HTTP error codes to ignore'), allow_empty=False, orientation='horizontal', valuespec=Integer(size=3, minvalue=0, maxvalue=999), @@ -552,21 +558,19 @@ _option_proxy_auth = ('proxy_auth', )) _options_proxy = ('http_proxy', - CascadingDropdown( + Alternative( title=_('Configure proxy server'), - sorted=False, - choices=[ - ('http_proxy', _('Use proxy'), - Dictionary( - title='Use proxy', - elements=[ - _option_proxy_server, - _option_proxy_auth, - ], - required_keys=['proxy_server'], - )), - ('--noproxy', _('Don\'t use any proxy')), - ('', _('Don\'t configure an proxy (use system settings)')), + elements=[ + Dictionary( + title='Use proxy', + elements=[ + _option_proxy_server, + _option_proxy_auth, + ], + required_keys=['proxy_server'], + ), + FixedValue('--noproxy', title=_('Don\'t use any proxy'), totext=_('')), + FixedValue(None, title=_('Don\'t configure an proxy (use system settings)'), totext=_('')), ], )) @@ -626,7 +630,7 @@ _option_address_resolution = ('ip_address_resolution', ('--ipv4', _('IPv4 only')), ('--ipv6', _('IPv6 only')), ])), - ('ens_source_interface', TextUnicode( + ('dns_source_interface', TextUnicode( title=_('Source interface'), regex='[0-9a-zA-Z]', size=15, @@ -750,12 +754,11 @@ _option_expected_strings = ('expected_strings', valuespec=TextInput(allow_empty=False, regex='[a-zA-Z0-9\.]'), )) _url_expected_strings = ('expected_strings', - CascadingDropdown( - title=_('Strings to expect in response'), - sorted=False, - choices=[ - ('expected_strings', _('Expected strings'), _option_expected_strings[1]), - ('', _('Don\'t expect any strings in the response')), + Alternative( + title=_('Override strings to expect in response'), + elements=[ + _option_expected_strings[1], + FixedValue(None, title=_('Don\'t expect any strings in the response'), totext=_('')), ], )) @@ -767,12 +770,11 @@ _option_header_strings = ('header_strings', valuespec=TextInput(allow_empty=False, regex='[a-zA-Z0-9\\.]'), )) _url_header_strings = ('header_strings', - CascadingDropdown( - title=_('Strings to expect in header'), - sorted=False, - choices=[ - ('header_strings', _('Header strings'), _option_expected_strings[1]), - ('', _('Don\'t expect any strings in the header')), + Alternative( + title=_('Override strings to expect in header'), + elements=[ + _option_expected_strings[1], + FixedValue(None, title=_('Don\'t expect any strings in the header'), totext=_('')), ], )) @@ -804,15 +806,13 @@ _option_request_headers = ('request_headers', add_label=_('Add header'), movable=True, )) - _url_request_headers = ('request_headers', - CascadingDropdown( - title=_('Set headers'), - sorted=False, - choices=[ - ('request_headers', _('Override default request headers'), _option_request_headers[1]), - ('', _('Don\'t configure request headers')), - ], + Alternative( + title=_('Override default headers'), + elements=[ + _option_request_headers[1], + FixedValue(None, title=_('Don\'t configure request headers'), totext=_('')) + ] )) _option_mail_settings = ('mail_settings', @@ -875,12 +875,11 @@ _option_api_key_header = ('api_key_header', ], )) _url_api_key_header = ('api_key_header', - CascadingDropdown( + Alternative( title=_('Set API key header'), - sorted=False, - choices=[ - ('api_key_header', _('API key header'), _option_api_key_header[1],), - ('', _('Don\'t configure an API key header')), + elements=[ + _option_api_key_header[1], + FixedValue(None, title=_('Don\'t configure an API key header'), totext=_('')), ], )) @@ -907,12 +906,11 @@ _option_limit_rate = ('limit_rate', orientation='horizontal', )) _url_limit_rate = ('limit_rate', - CascadingDropdown( + Alternative( title=_('Maximum UP-/Download rate'), - sorted=False, - choices=[ - ('limit_rate', _('Override default rate limit'), _option_limit_rate[1],), - ('', _('No rate limit')), + elements=[ + _option_limit_rate[1], + FixedValue(None, title=_('Don\'t configure a rate limit'), totext=_('')), ], )) @@ -939,12 +937,11 @@ _option_max_file_size = ('max_file_size', orientation='horizontal', )) _url_max_file_size = ('max_file_size', - CascadingDropdown( + Alternative( title=_('Maximum file size'), - sorted=False, - choices=[ - ('max_file_size', _('Override default maximum file size'), _option_max_file_size[1],), - ('', _('No file size limit')), + elements=[ + _option_max_file_size[1], + FixedValue(None, title=_('Don\'t configure a file size limit'), totext=_('')), ], )) @@ -956,13 +953,11 @@ _option_max_time = ('max_time', unit='s', )) _url_max_time = ('max_time', - CascadingDropdown( + Alternative( title=_('Maximum transfer time'), - sorted=False, - choices=[ - ('max_time', _('Override default maximum transfer time'), - _option_max_time[1],), - ('', _('No transfer time limit')), + elements=[ + _option_max_time[1], + FixedValue(None, title=_('Don\'t configure a transfer time limit'), totext=_('')), ], )) @@ -986,12 +981,11 @@ _option_speed_limit = ('speed_limit', ], orientation='horizontal', )) _url_speed_limit = ('speed_limit', - CascadingDropdown( + Alternative( title=_('Minimum speed'), - sorted=False, - choices=[ - ('speed_limit', _('Override default minimum speed'), _option_speed_limit[1],), - ('', _('No lower speed limit')), + elements=[ + _option_speed_limit[1], + FixedValue(None, title=_('Don\'t configure a lower speed limit'), totext=_('')), ], )) @@ -1002,13 +996,11 @@ _option_speed_time = ('speed_time', minvalue=1, unit='s', )) _url_speed_time = ('speed_time', - CascadingDropdown( + Alternative( title=_('Minimum speed time'), - sorted=False, - choices=[ - ('speed_time', _('Override default minimum speed time'), - _option_speed_time[1],), - ('', _('No minimum speed time limit')), + elements=[ + _option_speed_time[1], + FixedValue(None, title=_('Don\'t configure a minimum speed time limit'), totext=_('')), ], )) @@ -1019,13 +1011,11 @@ _option_connect_timeout = ('connect_timeout', minvalue=1, unit='s', )) _url_connect_timeout = ('connect_timeout', - CascadingDropdown( + Alternative( title=_('Maximum time to connect'), - sorted=False, - choices=[ - ('connect_timeout', _('Override default maximum time to connect'), - _option_connect_timeout[1],), - ('', _('Don\'t configure a maximum time to connect')), + elements=[ + _option_connect_timeout[1], + FixedValue(None, title=_('Don\'t configure a maximum time to connect'), totext=_('')), ], )) @@ -1044,6 +1034,21 @@ _option_limits = ('limits', ), title=_('Set connection limits') )) +_url_limits = ('limits', + Foldable( + Dictionary( + title=_('Limits'), + elements=[ + _url_limit_rate, + _url_max_file_size, + _url_connect_timeout, + _url_max_time, + _url_speed_limit, + _url_speed_time, + ] + ), + title=_('Override connection limits') + )) _option_user_agent = ('user_agent', TextUnicode( @@ -1073,14 +1078,12 @@ _option_referer = ('referer', forbidden_chars='|" ', )) _url_referer = ('referer', - CascadingDropdown( - title=_('Set referer'), - sorted=False, - choices=[ - ('referer', _('Override default referer'), - _option_referer[1],), - ('', _('Don\'t configure a referer')), - ], + Alternative( + title=_('Override default referer'), + elements=[ + _option_referer[1], + FixedValue(None, title=_('Don\'t configure a referer'), totext=_('')) + ] )) _options_compressed = ('compressed', @@ -1098,21 +1101,6 @@ _url_compressed = ('compressed', ], )) -_options_no_npn = ('no_npn', - FixedValue( - '--no-npn', - title=_('Next Protocol Negotiation (NPN)'), - totext=_('Next Protocol Negotiation disabled'), - )) -_url_no_npn = ('no_npn', - DropdownChoice( - title=_('Next Protocol Negotiation (NPN)'), - choices=[ - ('--no-npn', _('Disable Next Protocol Negotiation')), - ('', _('Don\'t disable Next Protocol Negotiation')), - ], - )) - _option_post = ('post_binary', Tuple( title=_('Send HTTP POST data'), @@ -1153,7 +1141,7 @@ _option_url_settings = ('url_settings', _url_user_agent, _url_header_strings, _url_expected_strings, - _option_limits, + _url_limits, _option_address_resolution, _option_ftp_settings, _option_mail_settings, @@ -1272,33 +1260,56 @@ _option_plugin_timeout = ('timeout', ), ),) -elements = [ - _option_url, - _option_plugin_interval, - _option_plugin_timeout, - _option_default_settings, -] +_option_curl_executable = ('curl_executable', + DropdownChoice( + title=_('cURL executable to use'), + help=_( + 'By default this plugin will use the system provided cURL executable. You can ' + 'decide to deploy a separate cURL executable. If you do so you need to install the ' + 'optional CMK package curl_executable<version>.mkp.' + ), + choices=[ + (None, _('Use system provided cURL executable')), + ('64bit', _('Deploy 64bit cURL version')), + ('32bit', _('Deploy 32bit cURL version')), + ] + )) + + +def _transform_forth_agent_config_curl(params): + # transform added 20220410 on removing os specific rules + if type(params) == tuple: + if params[0] in ['linux', 'windows', 'deploy']: + params = params[1] + else: + params = None + return params def _valuespec_agent_config_curl(): - return CascadingDropdown( - title=_('cURL'), - sorted=False, - choices=[ - ('linux', - _('Deploy Linux cURL agent plugin'), - Dictionary( - elements=elements, - required_keys=['url_list'], - )), - ('windows', - _('Deploy Windows cURL agent plugin'), - Dictionary( - elements=elements, - required_keys=['url_list'], - )), - (None, _('Do not deploy the cURL agent plugin')), - ], + return Transform( + Alternative( + title=_('cURL'), + elements=[ + Dictionary( + title=_('Deploy cURL agent plugin'), + elements=[ + _option_url, + _option_plugin_interval, + _option_plugin_timeout, + _option_curl_executable, + _option_default_settings, + ], + required_keys=['url_list'], + ), + FixedValue( + None, + title=_('Do not deploy the cURL agent plugin'), + totext=_('The cURL agent plugin will not be deployed') + ), + ], + ), + forth=_transform_forth_agent_config_curl, )