summaryrefslogtreecommitdiffstats
path: root/lib/ansible/module_utils/common/json.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/module_utils/common/json.py')
-rw-r--r--lib/ansible/module_utils/common/json.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/lib/ansible/module_utils/common/json.py b/lib/ansible/module_utils/common/json.py
new file mode 100644
index 0000000..727083c
--- /dev/null
+++ b/lib/ansible/module_utils/common/json.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019 Ansible Project
+# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+import datetime
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.common._collections_compat import Mapping
+from ansible.module_utils.common.collections import is_sequence
+
+
+def _is_unsafe(value):
+ return getattr(value, '__UNSAFE__', False) and not getattr(value, '__ENCRYPTED__', False)
+
+
+def _is_vault(value):
+ return getattr(value, '__ENCRYPTED__', False)
+
+
+def _preprocess_unsafe_encode(value):
+ """Recursively preprocess a data structure converting instances of ``AnsibleUnsafe``
+ into their JSON dict representations
+
+ Used in ``AnsibleJSONEncoder.iterencode``
+ """
+ if _is_unsafe(value):
+ value = {'__ansible_unsafe': to_text(value, errors='surrogate_or_strict', nonstring='strict')}
+ elif is_sequence(value):
+ value = [_preprocess_unsafe_encode(v) for v in value]
+ elif isinstance(value, Mapping):
+ value = dict((k, _preprocess_unsafe_encode(v)) for k, v in value.items())
+
+ return value
+
+
+def json_dump(structure):
+ return json.dumps(structure, cls=AnsibleJSONEncoder, sort_keys=True, indent=4)
+
+
+class AnsibleJSONEncoder(json.JSONEncoder):
+ '''
+ Simple encoder class to deal with JSON encoding of Ansible internal types
+ '''
+
+ def __init__(self, preprocess_unsafe=False, vault_to_text=False, **kwargs):
+ self._preprocess_unsafe = preprocess_unsafe
+ self._vault_to_text = vault_to_text
+ super(AnsibleJSONEncoder, self).__init__(**kwargs)
+
+ # NOTE: ALWAYS inform AWS/Tower when new items get added as they consume them downstream via a callback
+ def default(self, o):
+ if getattr(o, '__ENCRYPTED__', False):
+ # vault object
+ if self._vault_to_text:
+ value = to_text(o, errors='surrogate_or_strict')
+ else:
+ value = {'__ansible_vault': to_text(o._ciphertext, errors='surrogate_or_strict', nonstring='strict')}
+ elif getattr(o, '__UNSAFE__', False):
+ # unsafe object, this will never be triggered, see ``AnsibleJSONEncoder.iterencode``
+ value = {'__ansible_unsafe': to_text(o, errors='surrogate_or_strict', nonstring='strict')}
+ elif isinstance(o, Mapping):
+ # hostvars and other objects
+ value = dict(o)
+ elif isinstance(o, (datetime.date, datetime.datetime)):
+ # date object
+ value = o.isoformat()
+ else:
+ # use default encoder
+ value = super(AnsibleJSONEncoder, self).default(o)
+ return value
+
+ def iterencode(self, o, **kwargs):
+ """Custom iterencode, primarily design to handle encoding ``AnsibleUnsafe``
+ as the ``AnsibleUnsafe`` subclasses inherit from string types and
+ ``json.JSONEncoder`` does not support custom encoders for string types
+ """
+ if self._preprocess_unsafe:
+ o = _preprocess_unsafe_encode(o)
+
+ return super(AnsibleJSONEncoder, self).iterencode(o, **kwargs)