summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHervé Beraud <hberaud@redhat.com>2018-11-16 11:27:11 +0100
committerHervé Beraud <hberaud@redhat.com>2019-02-21 17:34:38 +0100
commit35dae9c288f7cf8310bf573106500fd317309da0 (patch)
treecc8a1f62876bf2f006f6cdf999298cff8bb40c71
parentd67f88623b439e28f014c6ff414cd6d81aa5fd43 (diff)
downloadoslo-serialization-35dae9c288f7cf8310bf573106500fd317309da0.tar.gz
Introduce a base yaml parser for all openstack components
A lot of openstack components like: - solum - tosca-parser - heat - murano - etc... reimplement theirs own yaml parser for loading and dumping. These implementations sometimes forgot to use a safe loader or safe dumper, our implementation use safe by default. You can deactive safe by passing the argument is_safe to false when you call oslo_serialization.yamlutils.load or oslo_serialization.yamlutils.dump. Change-Id: I63e85a2b4fc999e6acac12ae51c2ab8c64bddbc6 Co-Authored-By: Natal Ngétal <hobbestigrou@erakis.eu>
-rw-r--r--oslo_serialization/tests/test_yamlutils.py85
-rw-r--r--oslo_serialization/yamlutils.py86
-rw-r--r--requirements.txt1
3 files changed, 172 insertions, 0 deletions
diff --git a/oslo_serialization/tests/test_yamlutils.py b/oslo_serialization/tests/test_yamlutils.py
new file mode 100644
index 0000000..817ae8c
--- /dev/null
+++ b/oslo_serialization/tests/test_yamlutils.py
@@ -0,0 +1,85 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+import tempfile
+import textwrap
+import uuid
+
+from oslotest import base
+
+from oslo_serialization import yamlutils as yaml
+
+
+class BehaviorTestCase(base.BaseTestCase):
+
+ def test_loading(self):
+ payload = textwrap.dedent('''
+ - foo: bar
+ - list:
+ - [one, two]
+ - {check: yaml, in: test}
+ ''')
+ expected = [
+ {'foo': 'bar'},
+ {'list': None},
+ ['one', 'two'],
+ {'check': 'yaml', 'in': 'test'}
+ ]
+ loaded = yaml.load(payload)
+ self.assertEqual(loaded, expected)
+
+ def test_loading_with_unsafe(self):
+ payload = textwrap.dedent('''
+ !!python/object/apply:os.system ['echo "hello"']
+ ''')
+ loaded = yaml.load(payload, is_safe=False)
+ expected = 0
+ self.assertEqual(loaded, expected)
+
+ def test_dumps(self):
+ payload = [
+ {'foo': 'bar'},
+ {'list': None},
+ ['one', 'two'],
+ {'check': 'yaml', 'in': 'test'}
+ ]
+ dumped = yaml.dumps(payload)
+ expected = textwrap.dedent('''\
+ - {foo: bar}
+ - {list: null}
+ - [one, two]
+ - {check: yaml, in: test}
+ ''')
+ self.assertEqual(dumped, expected)
+
+ def test_dump(self):
+ payload = [
+ {'foo': 'bar'},
+ {'list': None},
+ ['one', 'two'],
+ {'check': 'yaml', 'in': 'test'}
+ ]
+ tmpfile = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
+ with open(tmpfile, 'w+') as fp:
+ yaml.dump(payload, fp)
+ with open(tmpfile, 'r') as fp:
+ file_content = fp.read()
+ expected = textwrap.dedent('''\
+ - foo: bar
+ - list: null
+ - - one
+ - two
+ - check: yaml
+ in: test
+ ''')
+ self.assertEqual(file_content, expected)
diff --git a/oslo_serialization/yamlutils.py b/oslo_serialization/yamlutils.py
new file mode 100644
index 0000000..921b74e
--- /dev/null
+++ b/oslo_serialization/yamlutils.py
@@ -0,0 +1,86 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""YAML related utilities.
+
+The main goal of this module is to standardize yaml management inside
+openstack. This module reduce technical debt by avoiding re-implementations
+of yaml manager in all the openstack projects.
+Use this module inside openstack projects to handle yaml securely and properly.
+"""
+
+import yaml
+
+
+def load(stream, is_safe=True):
+ """Converts a YAML document to a Python object.
+
+ :param stream: the YAML document to convert into a Python object. Accepts
+ a byte string, a Unicode string, an open binary file object,
+ or an open text file object.
+ :param is_safe: Turn off safe loading. True by default and only load
+ standard YAML. This option can be turned off by
+ passing ``is_safe=False`` if you need to load not only
+ standard YAML tags or if you need to construct an
+ arbitrary python object.
+
+ Stream specifications:
+
+ * An empty stream contains no documents.
+ * Documents are separated with ``---``.
+ * Documents may optionally end with ``...``.
+ * A single document may or may not be marked with ``---``.
+
+ Parses the given stream and returns a Python object constructed
+ from the first document in the stream. If there are no documents
+ in the stream, it returns None.
+ """
+ yaml_loader = yaml.Loader
+ if is_safe:
+ if hasattr(yaml, 'CSafeLoader'):
+ yaml_loader = yaml.CSafeLoader
+ else:
+ yaml_loader = yaml.SafeLoader
+ return yaml.load(stream, yaml_loader) # nosec B506
+
+
+def dumps(obj, is_safe=True):
+ """Converts a Python object to a YAML document.
+
+ :param obj: python object to convert into YAML representation.
+ :param is_safe: Turn off safe dumping.
+
+ Serializes the given Python object to a string and returns that string.
+ """
+ yaml_dumper = yaml.Dumper
+ if is_safe:
+ if hasattr(yaml, 'CSafeDumper'):
+ yaml_dumper = yaml.CSafeDumper
+ else:
+ yaml_dumper = yaml.SafeDumper
+ return yaml.dump(obj, Dumper=yaml_dumper)
+
+
+def dump(obj, fp, is_safe=True):
+ """Converts a Python object as a YAML document to ``fp``.
+
+ :param obj: python object to convert into YAML representation.
+ :param fp: a ``.write()``-supporting file-like object
+ :param is_safe: Turn off safe dumping.
+ """
+ yaml_dumper = yaml.Dumper
+ if is_safe:
+ if hasattr(yaml, 'CSafeDumper'):
+ yaml_dumper = yaml.CSafeDumper
+ else:
+ yaml_dumper = yaml.SafeDumper
+ return yaml.dump(obj, fp, default_flow_style=False, Dumper=yaml_dumper)
diff --git a/requirements.txt b/requirements.txt
index abdd118..8f3e493 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,4 @@ six>=1.10.0 # MIT
msgpack>=0.5.2 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
pytz>=2013.6 # MIT
+PyYAML>=3.12 # MIT