Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
host_key_checking = False
roles_path = ./roles
filter_plugins = ./filter_plugins
callback_plugins = ./callback_plugins
callback_result_format = yaml
stdout_callback = foremanctl
51 changes: 51 additions & 0 deletions src/callback_plugins/foremanctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule

DOCUMENTATION = """
name: foremanctl
type: stdout
short_description: foremanctl stdout callback
description:
- Suppresses default Ansible output for plays tagged with
foremanctl_suppress_default_output, displaying only task msg rather than ansible default output.
extends_documentation_fragment:
- default_callback
- result_format_callback
requirements:
- set as stdout in configuration
"""

class CallbackModule(DefaultCallbackModule):
"""Foremanctl callback."""

CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'foremanctl'

FALLBACK_TO_DEFAULT = True

def v2_playbook_on_start(self, playbook):
plays = playbook.get_plays()
tags = plays[0].tags
if 'foremanctl_suppress_default_output' in tags:
self.FALLBACK_TO_DEFAULT = False
if self.FALLBACK_TO_DEFAULT:
super().v2_playbook_on_start(playbook)

def v2_playbook_on_play_start(self, play):
if self.FALLBACK_TO_DEFAULT:
super().v2_playbook_on_play_start(play)

def v2_playbook_on_task_start(self, task, is_conditional):
if self.FALLBACK_TO_DEFAULT:
super().v2_playbook_on_task_start(task, is_conditional)

def v2_runner_on_ok(self, result):
if self.FALLBACK_TO_DEFAULT:
super().v2_runner_on_ok(result)
else:
if msg := result._result.get('msg'):
self._display.display(msg)

def v2_playbook_on_stats(self, stats):
if self.FALLBACK_TO_DEFAULT:
super().v2_playbook_on_stats(stats)
18 changes: 18 additions & 0 deletions src/filter_plugins/foremanctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ def available_foreman_plugins(_value):
plugins = [FEATURE_MAP.get(feature).get('foreman', {}).get('plugin_name') for feature in FEATURE_MAP.keys()]
return compact_list(plugins)

def list_all_features(enabled_features, only_enabled=False):
enabled_list = []
available_list = []
for name, meta in FEATURE_MAP.items():
if meta.get('internal', False):
continue
description = meta.get('description', '')
if name in enabled_features:
enabled_list.append((name, 'enabled', description))
elif not only_enabled:
available_list.append((name, 'available', description))

output = [f"{'FEATURE':<25} {'STATE':<12} DESCRIPTION"]
for name, state, description in enabled_list + available_list:
output.append(f"{name:<25} {state:<12} {description}")

return "\n".join(output)

def foreman_proxy_plugins(value):
dependencies = list(get_dependencies(filter_features(value)))
Expand All @@ -76,4 +93,5 @@ def filters(self):
'available_foreman_plugins': available_foreman_plugins,
'features_to_foreman_proxy_plugins': foreman_proxy_plugins,
'available_foreman_proxy_plugins': available_foreman_proxy_plugins,
'list_all_features': list_all_features,
}
16 changes: 16 additions & 0 deletions src/playbooks/features/features.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
- name: List features
hosts: quadlet
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory this shouldn't need to be locked to quadlet, but we can leave it for now as that's a bigger question we need to answer across multiple playbooks.

gather_facts: false
tags:
- foremanctl_suppress_default_output
vars:
list_enabled: false
vars_files:
- "../../vars/defaults.yml"
- "../../vars/flavors/{{ flavor }}.yml"
- "../../vars/base.yaml"
tasks:
- name: Print features
ansible.builtin.debug:
msg: "{{ enabled_features | list_all_features(list_enabled) }}"
9 changes: 9 additions & 0 deletions src/playbooks/features/metadata.obsah.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
help: List all enabled and available features

variables:
list_enabled:
parameter: --list-enabled
help: List only enabled features
action: store_true
persist: false
22 changes: 22 additions & 0 deletions tests/features_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import subprocess

def test_foremanctl_features():
command = ['./foremanctl', 'features']
result = subprocess.run(command, capture_output=True, text=True)

assert result.returncode == 0

for noise in ['PLAY [', 'TASK [', 'ok:', 'changed:', 'PLAY RECAP']:
assert noise not in result.stdout, f"Ansible output not suppressed: found '{noise}'"

for feature in ['foreman', 'foreman-proxy', 'azure-rm']:
assert feature in result.stdout, f"Expected feature '{feature}' in output"

def test_foremanctl_features_list_enabled():
command = ['./foremanctl', 'features', '--list-enabled']
result = subprocess.run(command, capture_output=True, text=True)

assert result.returncode == 0

assert 'enabled' in result.stdout
assert 'available' not in result.stdout