From 6aaac6cab3432c256b090744dc1712a1c1dbbba1 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Thu, 10 Oct 2024 14:22:21 -0400 Subject: [PATCH] service_facts: add FreeBSD support (#82556) new freebsd scanner using 'their version' of service cli tool also avoid loading the linuxy service scanner when freebsd --- changelogs/fragments/service_facts_fbsd.yml | 2 + lib/ansible/modules/service_facts.py | 78 ++++++++++++++++++- .../integration/targets/service_facts/aliases | 1 - 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/service_facts_fbsd.yml diff --git a/changelogs/fragments/service_facts_fbsd.yml b/changelogs/fragments/service_facts_fbsd.yml new file mode 100644 index 00000000000..6f06ab79f23 --- /dev/null +++ b/changelogs/fragments/service_facts_fbsd.yml @@ -0,0 +1,2 @@ +minor_changes: + - service_facts module got freebsd support added. diff --git a/lib/ansible/modules/service_facts.py b/lib/ansible/modules/service_facts.py index e37ddd69b37..fa0e5f22252 100644 --- a/lib/ansible/modules/service_facts.py +++ b/lib/ansible/modules/service_facts.py @@ -105,6 +105,8 @@ ansible_facts: import os import platform import re +import sys + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.locale import get_best_parsable_locale from ansible.module_utils.service import is_systemd_managed @@ -241,15 +243,17 @@ class ServiceScanService(BaseService): self.rc_status_path = self.module.get_bin_path("rc-status") self.rc_update_path = self.module.get_bin_path("rc-update") - # TODO: review conditionals ... they should not be this 'exclusive' if self.service_path and self.chkconfig_path is None and self.rc_status_path is None: self._list_sysvinit(services) + + # TODO: review conditionals ... they should not be this 'exclusive' if self.initctl_path and self.chkconfig_path is None: self._list_upstart(services) elif self.chkconfig_path: self._list_rh(services) elif self.rc_status_path is not None and self.rc_update_path is not None: self._list_openrc(services) + return services @@ -419,11 +423,81 @@ class OpenBSDScanService(BaseService): return services +class FreeBSDScanService(BaseService): + + _pid_regex = r'.+ is running as pid (\d+)\.' + + def get_info(self, service): + + service_info = {'status': 'unknown'} + rc, stdout, stderr = self.module.run_command("%s %s describe" % (self.service, service)) + if rc == 0: + service_info['description'] = stdout + rc, stdout, stderr = self.module.run_command("%s %s status" % (self.service, service)) + if rc == 0: + service_info['status'] = 'running' + p = re.compile(r'^\s?%s is running as pid (\d+).' % service) + matches = p.match(stdout[0]) + if matches: + # does not always get pid output + service_info['pid'] = matches[0] + else: + service_info['pid'] = 'N/A' + elif rc == 1: + if stdout and 'is not running' in stdout.splitlines()[0]: + service_info['status'] = 'stopped' + elif stderr and 'unknown directive' in stderr.splitlines()[0]: + service_info['status'] = 'unknown' + self.module.warn('Status query not supported for %s' % service) + else: + service_info['status'] = 'unknown' + out = stderr if stderr else stdout + self.module.warn('Could not retrieve status for %s: %s' % (service, out)) + else: + out = stderr if stderr else stdout + self.module.warn("Failed to get info for %s, no system message (rc=%s): %s" % (service, rc, out)) + + return service_info + + def get_enabled(self): + + services = [] + rc, stdout, stderr = self.module.run_command("%s -e" % (self.service)) + if rc == 0: + for line in stdout.splitlines(): + if line.startswith('/'): + services.append(os.path.basename(line)) + elif stderr: + self.module.warn("Failed to get services: %s" % stderr) + elif stdout: + self.module.warn("Failed to get services: %s" % stdout) + else: + self.module.warn("Failed to get services, no system message: rc=%s" % rc) + + return services + + def gather_services(self): + + services = {} + if sys.platform.startswith('freebsd'): + self.service = self.module.get_bin_path("service") + if self.service: + for svc in self.get_enabled(): + services[svc] = self.get_info(svc) + return services + + def main(): module = AnsibleModule(argument_spec=dict(), supports_check_mode=True) locale = get_best_parsable_locale(module) module.run_command_environ_update = dict(LANG=locale, LC_ALL=locale) - service_modules = (ServiceScanService, SystemctlScanService, AIXScanService, OpenBSDScanService) + + if sys.platform.startswith('freebsd'): + # frebsd is not compatible but will match other classes + service_modules = (FreeBSDScanService,) + else: + service_modules = (ServiceScanService, SystemctlScanService, AIXScanService, OpenBSDScanService) + all_services = {} for svc_module in service_modules: svcmod = svc_module(module) diff --git a/test/integration/targets/service_facts/aliases b/test/integration/targets/service_facts/aliases index 32e10b03cfa..f5edf4b1172 100644 --- a/test/integration/targets/service_facts/aliases +++ b/test/integration/targets/service_facts/aliases @@ -1,3 +1,2 @@ shippable/posix/group2 -skip/freebsd skip/macos