-
Notifications
You must be signed in to change notification settings - Fork 22
List enabled and available features #404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| from __future__ import (absolute_import, division, print_function) | ||
| __metaclass__ = type | ||
|
|
||
| from ansible.plugins.callback import CallbackBase | ||
| import pathlib | ||
| import yaml | ||
| import os | ||
|
|
||
| BASE_DIR = pathlib.Path(__file__).parent.parent | ||
| STATE_DIR = pathlib.Path(os.environ.get('OBSAH_STATE', '.var/lib/foremanctl')) | ||
|
|
||
| def load_yaml(path: pathlib.Path) -> dict: | ||
| try: | ||
| with path.open() as f: | ||
| return yaml.safe_load(f) or {} | ||
| except FileNotFoundError: | ||
| return {} | ||
|
|
||
|
|
||
| class CallbackModule(CallbackBase): | ||
| """Features listing callback.""" | ||
|
|
||
| CALLBACK_VERSION = 2.0 | ||
| CALLBACK_TYPE = 'stdout' | ||
| CALLBACK_NAME = 'foremanctl_features' | ||
|
|
||
| def __init__(self): | ||
| super().__init__() | ||
| self.feature_metadata = self._load_features_metadata() | ||
|
|
||
| def _load_features_metadata(self): | ||
| features_yaml = BASE_DIR / 'features.yaml' | ||
| return load_yaml(features_yaml) | ||
|
|
||
| def v2_runner_on_ok(self, result): | ||
| if result._task.action in ('ansible.builtin.debug', 'debug'): | ||
| self._display_features() | ||
|
|
||
| def _display_features(self): | ||
| params_yaml = STATE_DIR / "parameters.yaml" | ||
| params = load_yaml(params_yaml) | ||
|
|
||
| flavor = params.get('flavor') | ||
| added_features = params.get('features', []) | ||
|
|
||
| flavor_file = BASE_DIR / f"vars/flavors/{flavor}.yml" | ||
| flavor_config = load_yaml(flavor_file) | ||
| flavor_features = flavor_config.get('flavor_features', []) | ||
|
|
||
| enabled_features = flavor_features + added_features | ||
|
|
||
| enabled_list = [] | ||
| available_list = [] | ||
|
|
||
| for name in sorted(self.feature_metadata.keys()): | ||
| meta = self.feature_metadata.get(name, {}) or {} | ||
|
|
||
| if meta.get('internal', False): | ||
| continue | ||
|
|
||
| description = meta.get('description', '') | ||
|
|
||
| if name in enabled_features: | ||
| enabled_list.append((name, 'enabled', description)) | ||
| else: | ||
| available_list.append((name, 'available', description)) | ||
|
|
||
| self._display.display(f"{'FEATURE':<25} {'STATE':<12} DESCRIPTION") | ||
|
|
||
| for name, state, description in enabled_list: | ||
| self._display.display(f"{name:<25} {state:<12} {description}") | ||
|
|
||
| for name, state, description in available_list: | ||
| self._display.display(f"{name:<25} {state:<12} {description}") | ||
|
|
||
| total = len(enabled_list) + len(available_list) | ||
| self._display.display("") | ||
| self._display.display( | ||
| f"{total} features listed ({len(enabled_list)} enabled, {len(available_list)} available)." | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| #!/usr/bin/env python3 | ||
| """Display enabled and available features for Foreman deployment.""" | ||
| import yaml | ||
| import pathlib | ||
| import os | ||
|
|
||
| BASE_DIR = pathlib.Path(__file__).resolve().parent | ||
| STATE_DIR = pathlib.Path(os.environ.get('OBSAH_STATE', '.var/lib/foremanctl')) | ||
|
|
||
|
|
||
| def load_yaml(path: pathlib.Path) -> dict: | ||
| """Load YAML file, return empty dict if not found.""" | ||
| try: | ||
| with path.open() as f: | ||
| return yaml.safe_load(f) or {} | ||
| except FileNotFoundError: | ||
| return {} | ||
|
|
||
|
|
||
| def main(): | ||
| features_yaml = BASE_DIR / "features.yaml" | ||
| all_features = load_yaml(features_yaml) | ||
|
|
||
| # Load persisted parameters | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of the comments in the file I'd avoid. The code speaks for itself and the comment becomes noise. |
||
| params_yaml = STATE_DIR / "parameters.yaml" | ||
| params = load_yaml(params_yaml) | ||
|
|
||
| flavor = params.get('flavor', 'katello') | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am hesitant to have a default flavor.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, i can omit that |
||
| added_features = params.get('features', []) | ||
|
|
||
| # Load flavor configuration | ||
| flavor_file = BASE_DIR / f"vars/flavors/{flavor}.yml" | ||
| flavor_config = load_yaml(flavor_file) | ||
| flavor_features = flavor_config.get('flavor_features', []) | ||
|
|
||
| enabled_features = flavor_features + added_features | ||
|
|
||
| print(f"{'FEATURE':<25} {'STATE':<12} DESCRIPTION") | ||
|
|
||
| # Separate enabled and available | ||
| enabled_list = [] | ||
| available_list = [] | ||
|
|
||
| for name in sorted(all_features.keys()): | ||
| meta = all_features[name] or {} | ||
|
|
||
| # Skip internal features | ||
stejskalleos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if meta.get('internal', False): | ||
| continue | ||
|
|
||
| description = meta.get('description', '') | ||
|
|
||
| if name in enabled_features: | ||
| enabled_list.append((name, 'enabled', description)) | ||
| else: | ||
| available_list.append((name, 'available', description)) | ||
|
|
||
| for name, state, description in enabled_list: | ||
| print(f"{name:<25} {state:<12} {description}") | ||
|
|
||
| for name, state, description in available_list: | ||
| print(f"{name:<25} {state:<12} {description}") | ||
|
|
||
| print() | ||
| enabled_count = len(enabled_list) | ||
| total_count = len(enabled_list) + len(available_list) | ||
| print(f"{total_count} features listed ({enabled_count} enabled, {len(available_list)} available).") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| - name: List features | ||
| hosts: quadlet | ||
| gather_facts: false | ||
|
|
||
| tasks: | ||
| - name: List Enabled features | ||
| ansible.builtin.debug: | ||
| msg: "" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| help: | | ||
| List all enabled and available features | ||
arvind4501 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| stdout_callback: foremanctl_features | ||
Uh oh!
There was an error while loading. Please reload this page.