From 5cbc8b7ada76b861e9477ecf99a009fc816ae230 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Tue, 18 Apr 2023 13:03:04 +0200 Subject: [PATCH 1/3] New utils/facts.py: Provide facts about the repo like role and module lists The list of modules and roles is needed in several scripts now, therefore it makes sense to have one place for this. Here are the current variables: BASE_DIR: Base directory of the repo ROLES: List of roles in the roles folder MANAGEMENT_MODULES: List of management modules in the plugins/modules folder ROLES_MODULES: List of modules in the roles/*/library folders ALL_MODULES: List of all modules, the management and the roles modules All lists are sorted. --- utils/facts.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 utils/facts.py diff --git a/utils/facts.py b/utils/facts.py new file mode 100644 index 00000000..36871234 --- /dev/null +++ b/utils/facts.py @@ -0,0 +1,41 @@ +import os + + +def get_roles(dir): + roles = [] + + _rolesdir = "%s/roles/" % dir + for _role in os.listdir(_rolesdir): + _roledir = "%s/%s" % (_rolesdir, _role) + if not os.path.isdir(_roledir) or \ + not os.path.isdir("%s/meta" % _roledir) or \ + not os.path.isdir("%s/tasks" % _roledir): + continue + roles.append(_role) + + return sorted(roles) + + +def get_modules(dir): + management_modules = [] + roles_modules = [] + + for root, _dirs, files in os.walk(dir): + if not root.startswith("%s/plugins/" % dir) and \ + not root.startswith("%s/roles/" % dir): + continue + for _file in files: + if _file.endswith(".py"): + if root == "%s/plugins/modules" % dir: + management_modules.append(_file[:-3]) + elif root.startswith("%s/roles/" % dir): + if root.endswith("/library"): + roles_modules.append(_file[:-3]) + + return sorted(management_modules), sorted(roles_modules) + + +BASE_DIR = os.path.abspath(os.path.dirname(__file__) + "/..") +ROLES = get_roles(BASE_DIR) +MANAGEMENT_MODULES, ROLES_MODULES = get_modules(BASE_DIR) +ALL_MODULES = sorted(MANAGEMENT_MODULES + ROLES_MODULES) From 892c0dd6f0feb789599b0bb66c6f6c7cb46d3fbe Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 14 Apr 2023 18:49:39 +0200 Subject: [PATCH 2/3] utils/galaxyfy.py: Handle module_defaults, match roles and modules The section module_defaults was not handled by utils/galaxyfy.py, also there was no verification that only roles and modules provided by ansible-freeipa are matched for prepending the collection prefix. --- utils/galaxyfy.py | 80 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/utils/galaxyfy.py b/utils/galaxyfy.py index bc5c16da..7fef0515 100644 --- a/utils/galaxyfy.py +++ b/utils/galaxyfy.py @@ -4,7 +4,7 @@ # Authors: # Thomas Woerner # -# Copyright (C) 2019,2020 Red Hat +# Copyright (C) 2019-2023 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or modify @@ -21,49 +21,95 @@ # along with this program. If not, see . import re +from facts import ROLES, ALL_MODULES + + +def get_indent(txt): + return len(txt) - len(txt.lstrip()) def galaxyfy_playbook(project_prefix, collection_prefix, lines): - po1 = re.compile('(%s.*:)$' % project_prefix) - po2 = re.compile('(.*:) (%s.*)$' % project_prefix) + po_module = re.compile('(%s.*):$' % project_prefix) + po_module_arg = re.compile('(%s.*): (.*)$' % project_prefix) + po_module_unnamed = re.compile('- (%s.*):$' % project_prefix) + po_role = re.compile('(.*:) (%s.*)$' % project_prefix) + + pattern_module = r'%s.\1:' % collection_prefix + pattern_module_arg = r'%s.\1: \2' % collection_prefix + pattern_module_unnamed = r'- %s.\1:' % collection_prefix + pattern_role = r'\1 %s.\2' % collection_prefix + out_lines = [] - - pattern1 = r'%s.\1' % collection_prefix - pattern2 = r'\1 %s.\2' % collection_prefix - changed = False changeable = False include_role = False + module_defaults = False + module_defaults_indent = -1 for line in lines: stripped = line.strip() if stripped.startswith("- name:") or \ stripped.startswith("- block:"): changeable = True + module_defaults = False + module_defaults_indent = -1 elif stripped in ["set_fact:", "ansible.builtin.set_fact:", "vars:"]: changeable = False include_role = False + module_defaults = False + module_defaults_indent = -1 elif stripped == "roles:": changeable = True include_role = False + module_defaults = False + module_defaults_indent = -1 elif (stripped.startswith("include_role:") or stripped.startswith("ansible.builtin.include_role:")): include_role = True + module_defaults = False + module_defaults_indent = -1 elif include_role and stripped.startswith("name:"): - line = po2.sub(pattern2, line) - changed = True + match = po_role.search(line) + if match and match.group(2) in ROLES: + line = po_role.sub(pattern_role, line) + changed = True + elif stripped == "module_defaults:": + changeable = True + include_role = False + module_defaults = True + module_defaults_indent = -1 + elif module_defaults: + _indent = get_indent(line) + if module_defaults_indent == -1: + module_defaults_indent = _indent + if _indent == module_defaults_indent: + # only module, no YAML anchor or alias + match = po_module.search(line) + if match and match.group(1) in ALL_MODULES: + line = po_module.sub(pattern_module, line) + changed = True + # module with YAML anchor or alias + match = po_module_arg.search(line) + if match and match.group(1) in ALL_MODULES: + line = po_module_arg.sub(pattern_module_arg, line) + changed = True elif changeable and stripped.startswith("- role:"): - line = po2.sub(pattern2, line) - changed = True + match = po_role.search(line) + if match and match.group(2) in ROLES: + line = po_role.sub(pattern_role, line) + changed = True elif (changeable and stripped.startswith(project_prefix) - and not stripped.startswith(collection_prefix) # noqa and stripped.endswith(":")): # noqa - line = po1.sub(pattern1, line) - changed = True - changeable = False # Only change first line in task + match = po_module.search(line) + if match and match.group(1) in ALL_MODULES: + line = po_module.sub(pattern_module, line) + changed = True + changeable = False # Only change first line in task elif (stripped.startswith("- %s" % project_prefix) and stripped.endswith(":")): # noqa - line = po1.sub(pattern1, line) - changed = True + match = po_module_unnamed.search(line) + if match and match.group(1) in ALL_MODULES: + line = po_module_unnamed.sub(pattern_module_unnamed, line) + changed = True out_lines.append(line) From 966797dbee3848030653e972e94d3b450bb928ea Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Mon, 17 Apr 2023 16:49:22 +0200 Subject: [PATCH 3/3] utils/build-galaxy-release.sh: Create module action group The module action group .modules is created automatically while building the galaxy release. The action group can be used for module_defaults in this way: module_defauls: group/.modules: ipaadmin_password: SomeADMINpassword Example: module_defaults: group/freeipa.ansible_freeipa.modules: ipaadmin_password: SomeADMINpassword ipaapi_context: "{{ ipa_context | default(omit) }}" collections: - freeipa.ansible_freeipa --- utils/build-galaxy-release.sh | 2 ++ utils/create_action_group.py | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 utils/create_action_group.py diff --git a/utils/build-galaxy-release.sh b/utils/build-galaxy-release.sh index bbce0122..e3eb0b9c 100755 --- a/utils/build-galaxy-release.sh +++ b/utils/build-galaxy-release.sh @@ -114,6 +114,8 @@ echo -e "\033[ACreating CHANGELOG.rst... \033[32;1mDONE\033[0m" sed -i -e "s/ansible.module_utils.ansible_freeipa_module/ansible_collections.${collection_prefix}.plugins.module_utils.ansible_freeipa_module/" plugins/modules/*.py +python utils/create_action_group.py "meta/runtime.yml" "$collection_prefix" + (cd plugins/module_utils && { ln -sf ../../roles/*/module_utils/*.py . }) diff --git a/utils/create_action_group.py b/utils/create_action_group.py new file mode 100644 index 00000000..6f6cec26 --- /dev/null +++ b/utils/create_action_group.py @@ -0,0 +1,24 @@ +import sys +import yaml +from facts import MANAGEMENT_MODULES + + +def create_action_group(yml_file, project_prefix): + yaml_data = None + with open(yml_file) as f_in: + yaml_data = yaml.safe_load(f_in) + + yaml_data.setdefault("action_groups", {})[ + "%s.modules" % project_prefix + ] = MANAGEMENT_MODULES + + with open(yml_file, 'w') as f_out: + yaml.safe_dump(yaml_data, f_out, default_flow_style=False, + explicit_start=True) + + +if len(sys.argv) != 3: + print("Usage: %s " % sys.argv[0]) + sys.exit(-1) + +create_action_group(sys.argv[1], sys.argv[2])