diff --git a/changelogs/fragments/750-jc-new-filter.yaml b/changelogs/fragments/750-jc-new-filter.yaml new file mode 100644 index 0000000000..d4ff7845f7 --- /dev/null +++ b/changelogs/fragments/750-jc-new-filter.yaml @@ -0,0 +1,2 @@ +minor_changes: +- jc - new filter to convert the output of many shell commands and file-types to JSON. Uses the jc library at https://github.com/kellyjonbrazil/jc. For example, filtering the STDOUT output of ``uname -a`` via ``{{ result.stdout | community.general.jc('uname') }}``. Requires Python 3.6+ (https://github.com/ansible-collections/community.general/pull/750). diff --git a/plugins/filter/jc.py b/plugins/filter/jc.py new file mode 100644 index 0000000000..e854128f67 --- /dev/null +++ b/plugins/filter/jc.py @@ -0,0 +1,94 @@ +# (c) 2015, Filipe Niero Felisbino +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# +# contributed by Kelly Brazil + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.errors import AnsibleError, AnsibleFilterError +import importlib + +try: + import jc + HAS_LIB = True +except ImportError: + HAS_LIB = False + + +def jc(data, parser, quiet=True, raw=False): + """Convert returned command output to JSON using the JC library + + Arguments: + + parser required (string) the correct parser for the input data (e.g. 'ifconfig') + see https://github.com/kellyjonbrazil/jc#parsers for latest list of parsers. + quiet optional (bool) True to suppress warning messages (default is True) + raw optional (bool) True to return pre-processed JSON (default is False) + + Returns: + + dictionary or list of dictionaries + + Example: + + - name: run date command + hosts: ubuntu + tasks: + - shell: date + register: result + - set_fact: + myvar: "{{ result.stdout | community.general.jc('date') }}" + - debug: + msg: "{{ myvar }}" + + produces: + + ok: [192.168.1.239] => { + "msg": { + "day": 9, + "hour": 22, + "minute": 6, + "month": "Aug", + "month_num": 8, + "second": 22, + "timezone": "UTC", + "weekday": "Sun", + "weekday_num": 1, + "year": 2020 + } + } + """ + + if not HAS_LIB: + raise AnsibleError('You need to install "jc" prior to running jc filter') + + try: + jc_parser = importlib.import_module('jc.parsers.' + parser) + return jc_parser.parse(data, quiet=quiet, raw=raw) + + except Exception as e: + raise AnsibleFilterError('Error in jc filter plugin: %s' % e) + + +class FilterModule(object): + ''' Query filter ''' + + def filters(self): + return { + 'jc': jc + } diff --git a/tests/integration/targets/filter_jc/aliases b/tests/integration/targets/filter_jc/aliases new file mode 100644 index 0000000000..f49c8c7f6a --- /dev/null +++ b/tests/integration/targets/filter_jc/aliases @@ -0,0 +1,3 @@ +shippable/posix/group2 +skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller +skip/python2.7 # jc only supports python3.x diff --git a/tests/integration/targets/filter_jc/runme.sh b/tests/integration/targets/filter_jc/runme.sh new file mode 100755 index 0000000000..17c5beca9d --- /dev/null +++ b/tests/integration/targets/filter_jc/runme.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -eux + +source virtualenv.sh + +# Requirements have to be installed prior to running ansible-playbook +# because plugins and requirements are loaded before the task runs + +pip install jc + +ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" diff --git a/tests/integration/targets/filter_jc/runme.yml b/tests/integration/targets/filter_jc/runme.yml new file mode 100644 index 0000000000..c624d120bb --- /dev/null +++ b/tests/integration/targets/filter_jc/runme.yml @@ -0,0 +1,3 @@ +- hosts: localhost + roles: + - { role: filter_jc } diff --git a/tests/integration/targets/filter_jc/tasks/main.yml b/tests/integration/targets/filter_jc/tasks/main.yml new file mode 100644 index 0000000000..560ca551b0 --- /dev/null +++ b/tests/integration/targets/filter_jc/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: test jc key/value parser + assert: + that: + - "('key1=value1\nkey2=value2' | community.general.jc('kv')) == {'key1': 'value1', 'key2': 'value2'}"