Search code examples
ansibleansible-module

How to search for a string in a remote file using Ansible?


Based on a questions

and considerations like

  • By using the slurp module one is going to transfer the whole file from the Remote Node to the Control Node over the network just in order to process it and looking up a string. For log files these can be several MB and whereby one is mostly interested only in the information if the file on the Remote Node contains a specific string and therefore one would only need to transfer that kind of information, true or false.
  • How to execute a script on a Remote Node using Ansible?

I was wondering how this can be solved instead of using the shell module?

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    SEARCH_STRING: "test"
    SEARCH_FILE: "test.file"

  tasks:

  - name: Search for string in file
    command:
      cmd: "grep '{{ SEARCH_STRING }}' {{ SEARCH_FILE }}"
    register: result
    # Since it is a reporting task
    # which needs to deliver a result in any case
    failed_when: result.rc != 0 and result.rc != 1
    check_mode: false
    changed_when: false

Or instead of using a workaround with the lineinfile module?

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    SEARCH_STRING: "test"
    SEARCH_FILE: "test.file"

  tasks:

  - name: Search for string
    lineinfile:
      path: "{{ SEARCH_FILE }}"
      regexp: "{{ SEARCH_STRING }}"
      line: "SEARCH_STRING FOUND"
      state: present
    register: result
    # Since it is a reporting task
    changed_when: false
    failed_when: "'replaced' not in result.msg" # as it means SEARCH_STRING NOT FOUND
    check_mode: true # to prevent changes and to do a dry-run only

  - name: Show result, if not found
    debug:
      var: result
    when: "'added' in result.msg" # as it means SEARCH_STRING NOT FOUND

Since I am looking for a more generic approach for tasks in example like

grep ${SEARCH_STRING} /var/log/messages* | cut -d ':' -f 1 | uniq

could it be a feasible case for Should you develop a module?


Solution

  • Following Developing modules and Creating a module I've found the following simple solution with

    Custom Module library/pygrep.py

    #!/usr/bin/python
    
    from __future__ import (absolute_import, division, print_function)
    __metaclass__ = type
    
    from ansible.module_utils.basic import AnsibleModule
    
    def run_module():
        module_args = dict(
            path=dict(type='str', required=True),
            search_string=dict(type='str', required=True)
        )
    
        result = dict(
            changed=False,
            found_lines='',
            found=False
        )
    
        module = AnsibleModule(
            argument_spec=module_args,
            supports_check_mode=True
        )
    
        with open(module.params['path'], 'r') as f:
            for line in f.readlines():
                if module.params['search_string'] in line:
                    result['found_lines'] =  result['found_lines'] + line
                    result['found'] = True
    
        result['changed'] = False
    
        module.exit_json(**result)
    
    def main():
        run_module()
    
    if __name__ == '__main__':
        main()
    

    Playbook pygrep.yml

    ---
    - hosts: localhost
      become: false
      gather_facts: false
    
      vars:
    
        SEARCH_FILE: "test.file"
        SEARCH_STRING: "test"
    
      tasks:
    
      - name: Grep string from file
        pygrep:
          path: "{{ SEARCH_FILE }}"
          search_string: "{{ SEARCH_STRING }}"
        register: search
    
      - name: Show search
        debug:
          var: search
        when: search.found
    

    For a simple test.file

    NOTEST
    This is a test file.
    It contains several test lines.
    321tset
    123test
    cbatset
    abctest
    testabc
    test123
    END OF TEST
    

    it will result into an output of

    TASK [Show search] ******************
    ok: [localhost] =>
      search:
        changed: false
        failed: false
        found: true
        found_lines: |-
          This is a test file.
          It contains several test lines.
          123test
          abctest
          testabc
          test123
    
    

    Some Links