Bring over the 'safeyaml' from bzr

This will likely also be very useful to use since it
ensures we safely use/parse yaml.

Change-Id: Ibc0f7dee46d2f199e2d65d9bc6444bfa72383704
This commit is contained in:
Scott Moser 2015-04-02 12:34:42 -07:00 committed by Joshua Harlow
parent 0f723a9772
commit 792564e401
5 changed files with 169 additions and 17 deletions

82
cloudinit/logging.py Normal file
View File

@ -0,0 +1,82 @@
# Copyright 2015 Canonical Ltd.
# This file is part of cloud-init. See LICENCE file for license information.
#
# vi: ts=4 expandtab
from __future__ import absolute_import
import logging
import sys
_BASE = __name__.split(".", 1)[0]
# Add a BLATHER level, this matches the multiprocessing utils.py module (and
# kazoo and others) that declares a similar level, this level is for
# information that is even lower level than regular DEBUG and gives out so
# much runtime information that it is only useful by low-level/certain users...
BLATHER = 5
# Copy over *select* attributes to make it easy to use this module.
CRITICAL = logging.CRITICAL
DEBUG = logging.DEBUG
ERROR = logging.ERROR
FATAL = logging.FATAL
INFO = logging.INFO
NOTSET = logging.NOTSET
WARN = logging.WARN
WARNING = logging.WARNING
class _BlatherLoggerAdapter(logging.LoggerAdapter):
def blather(self, msg, *args, **kwargs):
"""Delegate a blather call to the underlying logger."""
self.log(BLATHER, msg, *args, **kwargs)
def warn(self, msg, *args, **kwargs):
"""Delegate a warning call to the underlying logger."""
self.warning(msg, *args, **kwargs)
# TODO(harlowja): we should remove when we no longer have to support 2.6...
if sys.version_info[0:2] == (2, 6):
class _FixedBlatherLoggerAdapter(_BlatherLoggerAdapter):
"""Ensures isEnabledFor() exists on adapters that are created."""
def isEnabledFor(self, level):
return self.logger.isEnabledFor(level)
_BlatherLoggerAdapter = _FixedBlatherLoggerAdapter
# Taken from python2.7 (same in python3.4)...
class _NullHandler(logging.Handler):
"""This handler does nothing.
It's intended to be used to avoid the
"No handlers could be found for logger XXX" one-off warning. This is
important for library code, which may contain code to log events. If a
user of the library does not configure logging, the one-off warning
might be produced; to avoid this, the library developer simply needs
to instantiate a _NullHandler and add it to the top-level logger of the
library module or package.
"""
def handle(self, record):
"""Stub."""
def emit(self, record):
"""Stub."""
def createLock(self):
self.lock = None
else:
_NullHandler = logging.NullHandler
def getLogger(name=_BASE, extra=None):
logger = logging.getLogger(name)
if not logger.handlers:
logger.addHandler(_NullHandler())
return _BlatherLoggerAdapter(logger, extra=extra)

40
cloudinit/safeyaml.py Normal file
View File

@ -0,0 +1,40 @@
# Copyright 2015 Canonical Ltd.
# This file is part of cloud-init. See LICENCE file for license information.
#
# vi: ts=4 expandtab
import yaml as _yaml
from cloudinit import util
YAMLError = _yaml.YAMLError
def load(path):
"""Load yaml string from a path and return the data represented.
Exception will be raised if types other than the following are found:
dict, int, float, string, list, unicode
"""
return loads(util.load_file(path))
def loads(blob):
"""Load yaml string and return the data represented.
Exception will be raised if types other than the following are found:
dict, int, float, string, list, unicode
"""
return _yaml.safe_load(blob)
def dumps(obj):
"""Dumps an object back into a yaml string."""
formatted = _yaml.safe_dump(obj,
line_break="\n",
indent=4,
explicit_start=True,
explicit_end=True,
default_flow_style=False)
return formatted

View File

@ -1,9 +1,12 @@
# Copyright 2015 Canonical Ltd.
# Copyright 2015 Cloudbase Solutions Srl
# This file is part of cloud-init. See LICENCE file for license information.
#
# vi: ts=4 expandtab
def main():
print("Hello World\n")
print("Run cloudinit run!")
# vi: ts=4 expandtab
if __name__ == '__main__':
main()

View File

@ -0,0 +1,29 @@
# Copyright 2015 Canonical Ltd.
# This file is part of cloud-init. See LICENCE file for license information.
#
# vi: ts=4 expandtab
from cloudinit import safeyaml as yaml
from cloudinit import test
class TestSafeYaml(test.TestCase):
def test_simple(self):
blob = '\nk1: one\nk2: two'
expected = {'k1': "one", 'k2': "two"}
self.assertEqual(yaml.loads(blob), expected)
def test_bogus_raises_exception(self):
badyaml = "1\n 2:"
self.assertRaises(yaml.YAMLError, yaml.loads, badyaml)
def test_unsafe_types(self):
# should not load complex types
unsafe_yaml = "!!python/object:__builtin__.object {}"
self.assertRaises(yaml.YAMLError, yaml.loads, unsafe_yaml)
def test_python_unicode_not_allowed(self):
# python/unicode is not allowed
# in the past this type was allowed, but not now, so explicit test.
blob = "{k1: !!python/unicode 'my unicode', k2: my string}"
self.assertRaises(yaml.YAMLError, yaml.loads, blob)

View File

@ -1,19 +1,17 @@
# Copyright (C) 2015 Canonical Ltd.
# Copyright (C) 2015 Cloudbase Solutions Srl
# Copyright 2015 Canonical Ltd.
# This file is part of cloud-init. See LICENCE file for license information.
#
# 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.
# vi: ts=4 expandtab
__all__ = ('abstractclassmethod', )
from cloudinit import logging
LOG = logging.getLogger(__name__)
def load_file(path, encoding='utf8'):
LOG.blather("Loading file from path '%s' (%s)", path, encoding)
with open(path, 'rb') as fh:
return fh.read().decode(encoding)
class abstractclassmethod(classmethod):