Search code examples
ansibleansible-inventoryansible-template

how to extract ps command in Ansible results in a list


- name: Check running processes
    shell: ps aux | grep -i "[t]omcat"
    register: tomcat_process_cmd

The result of the ps aux | grep ... command is (wrapped for readability):

tomcat  1183735  0.2 33.3 14596700 2631076 ?    Sl   May10   8:54
/usr/bin/java
-Djava.util.logging.config.file=/opt/apache/tomcat/9.0.85/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Dcom.sun.jndi.ldap.connect.pool.protocol=plain ssl
-Dcom.sun.jndi.ldap.connect.pool.authentication=none simple DIGEST-MD5
-Djdk.tls.ephemeralDHKeySize=5248
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources
-Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Xmx8192m
-Dignore.endorsed.dirs= -classpath
/opt/apache/tomcat/9.0.85/bin/bootstrap.jar:/opt/apache/tomcat/9.0.85/bin/tomcat-juli.jar
-Dcatalina.base=/opt/apache/tomcat/9.0.85
-Dcatalina.home=/opt/apache/tomcat/9.0.85
-Djava.io.tmpdir=/opt/apache/tomcat/9.0.85/temp
org.apache.catalina.startup.Bootstrap start

The value of tomcat_process_cmd.stdout_lines is stored in the dataresult variable:

  - name: tomcat process stdout
    set_fact: 
      dataresult: "{{ tomcat_process_cmd.stdout_lines}}"

I am trying to extract the catalina home,catalina base and logging properties from the ps aux | grep tomcat command, if multiple tomcat instances are running i would like to set them in a list

users:[useraA, userB]
catalinahome:[catalinahome1, catalinahome2]

I tried

  - name: Create dictionary for tomusers
    set_fact:
      tom_users: "{{tom_users | default([]) + [ item.split()[0]] }}"
    loop: "{{ dataresult }}" --- stdout_lines

for catalina home and base i am unable to retrieve the data please assist

here is what i tried

- name: Create dictionary for java.util.logging.config.file
    set_fact:
      log_propss: "{{ item.split()[8] | item.0.split('=') }}"
    loop: "{{ dataresult }}"
    when: tomcat_process_cmd.stdout | regex_search('logging.config.file')

Solution

  • If the output from the ps aux command produces what you show in your question, then each item in dataresult will be one line of output from ps.

    One path forward is to create a dictionary from all the -D arguments that appear in the command line. That is, we can do something like this:

    - name: Extract metadata from `ps` output
      set_fact:
        tomcat_users: "{{ tomcat_users|default([]) + [ item.split()[0]] }}"
        tomcat_info: "{{ tomcat_info|default([]) + [item.split() | select('match', '-D') | map('regex_replace', '-D', '') | map('split', '=') | community.general.dict] }}"
      loop: "{{ dataresult }}"
    
    - debug:
        msg:
          user: "{{ item.0 }}"
          catalina.home: "{{ item.1['catalina.home']|default('<unknown>') }}"
          catalina.base: "{{ item.1['catalina.base']|default('<unknown>') }}"
          logging: "{{ item.1['java.util.logging.config.file']|default('<unknown>')}}"
      loop: "{{ tomcat_users | zip(tomcat_info) }}"
      loop_control:
        label: "{{ item.0 }}"
    

    Note that we're using the community.general.dict filter, which you should have available in a typical Ansible install but if not you'll need to install the community.general collection.

    We're creating two lists:

    • tomcat_users is a list of usernames extracted from the first field of the ps output.
    • tomcat_info is a list of dictionaries extracted from the -D arguments in each command line.

    Running this against your sample data produces:

    TASK [debug] *********************************************************************************************************************************
    ok: [localhost] => (item=tomcat) => {
        "msg": {
            "catalina.base": "/opt/apache/tomcat/9.0.85",
            "catalina.home": "/opt/apache/tomcat/9.0.85",
            "logging": "/opt/apache/tomcat/9.0.85/conf/logging.properties",
            "user": "tomcat"
        }
    }
    

    If for someone reason you can't use community.general.dict, drop the following into filter_plugins/make_dict.py (adjacent to your playbook):

    def make_dict(v):
        return dict(v)
    
    
    class FilterModule:
        def filters(self):
            return {"make_dict": make_dict}
    

    And then replace community.general.dict in the above with make_dict.