from ansible.module_utils.basic import AnsibleModule import os import re def parse_mkinitcpio_line(type: str, line: str) -> list[str]: regex = "^" + type + r"=\((.*)\)$" result = re.search(regex, line) if result is None: return None return list(filter(lambda v: v != "", result.group(1).split(" "))) def build_mkinitcpio_line(type: str, values: list[str]) -> str: return type + "=(" + " ".join(values) + ")" def update_mkinitcpio_line(type: str, desired_state: str, values: list[str], line) -> tuple[str, bool]: current_values = parse_mkinitcpio_line(type, line) changed = False if current_values is not None: if desired_state == "present": for value in values: if value not in current_values: current_values.append(value) changed = True elif desired_state == "absent": for value in values: if value in current_values: current_values.remove(value) changed = True else: raise ValueError("Invalid state: %" % desired_state) return build_mkinitcpio_line(type, current_values), changed else: return None, False def run_module(): # define available arguments/parameters a user can pass to the module module_args = dict( state=dict(default='present', choices=['present', 'absent']), binaries=dict(type='list'), files=dict(type='list'), hooks=dict(type='list'), path=dict(type='str', default='/etc/mkinitcpio.conf') ) # seed the result dict in the object # we primarily care about changed and state # changed is if this module effectively modified the target # state will include any data that you want your module to pass back # for consumption, for example, in a subsequent task result = dict( changed=False, ) # the AnsibleModule object will be our abstraction working with Ansible # this includes instantiation, a couple of common attr would be the # args/params passed to the execution, as well as if the module # supports check mode module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) # if the user is working with this module in only check mode we do not # want to make any changes to the environment, just return the current # state with no modifications if module.check_mode: module.exit_json(**result) path = module.params['path'] if not os.path.isfile(path): module.fail_json(msg="The path is invalid: %s does not exist" % path) state = module.params['state'] binaries = module.params['binaries'] files = module.params['files'] hooks = module.params['hooks'] file = open(path, "r") lines = file.readlines() for index, line in enumerate(lines): if binaries is not None: updated_line, updated = update_mkinitcpio_line( "BINARIES", state, binaries, line) if updated: result['changed'] = True if updated_line is not None: lines[index] = updated_line if files is not None: updated_line, updated = update_mkinitcpio_line( "FILES", state, files, line) if updated: result['changed'] = True if updated_line is not None: lines[index] = updated_line if hooks is not None: updated_line, updated = update_mkinitcpio_line( "HOOKS", state, hooks, line) if updated: result['changed'] = True if updated_line is not None: lines[index] = updated_line if result['changed']: file = open(path, "w") file.write("".join(lines)) # manipulate or modify the state as needed (this is going to be the # part where your module will do what it needs to do) # in the event of a successful module execution, you will want to # simple AnsibleModule.exit_json(), passing the key/value results module.exit_json(**result) def main(): run_module() if __name__ == '__main__': main()