Files
opencafe/cafe/engine/models/data_interfaces.py
Jose Idar 43da54aca3 Moves root logger instantiation to the runner.
* Fixes logging from memoized methods, especially when those
   methods are run before unittest fixture instantiation.
   (e.g., data generators)
 * Changes ConfigParser data interface to log 'missing section'
   exceptions as errors if there is no default defined for that
   section.  (When defaulted, the exception is logged as a
   warning).
 * Modifies the memoized() decorator to log at the method level.
   Fixes a bug that was causing log duplication whenever a
   memoized method made a call to another memoized method.

Change-Id: I4ae8a104a222aae916059434c9339f9863b08186
2014-05-28 10:59:54 -05:00

298 lines
9.6 KiB
Python

"""
Copyright 2013 Rackspace
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 abc
import json
import os
import ConfigParser
from cafe.common.reporting import cclogging
try:
from cafe.engine.mongo.client import BaseMongoClient
except:
""" We are temporarily ignoring errors due to plugin refactor.
The mongo data-source is currently not being used. and needs to be
abstracted out into a data-source plugin.
"""
pass
class ConfigDataException(Exception):
pass
class NonExistentConfigPathError(Exception):
pass
class ConfigEnvironmentVariableError(Exception):
pass
# This is a decorator
def expected_values(*values):
def decorator(fn):
def wrapped():
class UnexpectedConfigOptionValueError(Exception):
pass
value = fn()
if value not in values:
raise UnexpectedConfigOptionValueError(value)
return fn()
return wrapped
return decorator
def _get_path_from_env(os_env_var):
try:
return os.environ[os_env_var]
except KeyError:
msg = "'{0}' environment variable was not set.".format(
os_env_var)
raise ConfigEnvironmentVariableError(msg)
except Exception as exception:
print ("Unexpected exception when attempting to access '{1}'"
" environment variable.".format(os_env_var))
raise exception
# Standard format to for flat key/value data sources
CONFIG_KEY = 'CAFE_{section_name}_{key}'
class DataSource(object):
__metaclass__ = abc.ABCMeta
def get(self, item_name, default=None):
raise NotImplementedError
def get_raw(self, item_name, default=None):
raise NotImplementedError
def get_boolean(self, item_name, default=None):
raise NotImplementedError
class EnvironmentVariableDataSource(DataSource):
def __init__(self, section_name):
self._log = cclogging.getLogger(
cclogging.get_object_namespace(self.__class__))
self._section_name = section_name
def get(self, item_name, default=None):
return os.environ.get(CONFIG_KEY.format(
section_name=self._section_name, key=item_name), default)
def get_raw(self, item_name, default=None):
return self.get(item_name, default)
def get_boolean(self, item_name, default=None):
item_value = self.get(item_name, default)
return bool(item_value) if item_value is not None else item_value
class ConfigParserDataSource(DataSource):
def __init__(self, config_file_path, section_name):
self._log = cclogging.getLogger(
cclogging.get_object_namespace(self.__class__))
self._data_source = ConfigParser.SafeConfigParser()
self._section_name = section_name
# Check if the path exists
if not os.path.exists(config_file_path):
msg = 'Could not verify the existence of config file at {0}'\
.format(config_file_path)
raise NonExistentConfigPathError(msg)
# Read the file in and turn it into a SafeConfigParser instance
try:
self._data_source.read(config_file_path)
except Exception as exception:
self._log.exception(exception)
raise exception
def get(self, item_name, default=None):
try:
return self._data_source.get(self._section_name, item_name)
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as e:
if default is None:
self._log.error(str(e))
else:
msg = "{0}. Using default value '{1}' instead".format(
str(e), default)
self._log.warning(msg)
return default
def get_raw(self, item_name, default=None):
try:
return self._data_source.get(
self._section_name, item_name, raw=True)
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as e:
if default is None:
self._log.error(str(e))
else:
msg = "{0}. Using default value '{1}' instead".format(
str(e), default)
self._log.warning(msg)
return default
def get_boolean(self, item_name, default=None):
try:
return self._data_source.getboolean(self._section_name, item_name)
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as e:
if default is None:
self._log.error(str(e))
else:
msg = "{0}. Using default value '{1}' instead".format(
str(e), default)
self._log.warning(msg)
return default
class DictionaryDataSource(DataSource):
def get(self, item_name, default=None):
section = self._data_source.get(self._section_name)
if section is None:
self._log.error("No section: '{section_name}'".format(
section_name=self._section_name))
return None
if item_name not in section:
self._log.error(
"No option '{item_name}' in section: '{section_name}'".format(
section_name=self._section_name, item_name=item_name))
return default
return section.get(item_name, default)
def get_raw(self, item_name, default=None):
section = self._data_source.get(self._section_name)
if section is None:
self._log.error("No section: '{section_name}'".format(
section_name=self._section_name))
return None
if item_name not in section:
self._log.error(
"No option '{item_name}' in section: '{section_name}'".format(
section_name=self._section_name, item_name=item_name))
return default
return section.get(item_name, default)
def get_boolean(self, item_name, default=None):
section = self._data_source.get(self._section_name)
if section is None:
self._log.error("No section: '{section_name}'".format(
section_name=self._section_name))
return None
if item_name not in section:
self._log.error(
"No option '{item_name}' in section: '{section_name}'".format(
section_name=self._section_name, item_name=item_name))
return default
item_value = section.get(item_name, default)
return bool(item_value) if item_value is not None else item_value
class JSONDataSource(DictionaryDataSource):
def __init__(self, config_file_path, section_name):
self._log = cclogging.getLogger(
cclogging.get_object_namespace(self.__class__))
self._section_name = section_name
# Check if file path exists
if not os.path.exists(config_file_path):
msg = 'Could not verify the existence of config file at {0}'\
.format(config_file_path)
raise NonExistentConfigPathError(msg)
with open(config_file_path) as config_file:
config_data = config_file.read()
try:
self._data_source = json.loads(config_data)
except Exception as exception:
self._log.exception(exception)
raise exception
class MongoDataSource(DictionaryDataSource):
def __init__(self, hostname, db_name, username, password,
config_name, section_name):
self._section_name = section_name
self.db = BaseMongoClient(
hostname=hostname, db_name=db_name,
username=username, password=password)
self.db.connect()
self.db.auth()
self._data_source = self.db.find_one({'config_name': config_name})
class BaseConfigSectionInterface(object):
"""Base class for building an interface for the data contained in a
SafeConfigParser object, as loaded from the config file as defined
by the engine's config file.
"""
def __init__(self, config_file_path, section_name):
self._override = EnvironmentVariableDataSource(
section_name)
self._data_source = ConfigParserDataSource(
config_file_path, section_name)
self._section_name = section_name
def get(self, item_name, default=None):
return self._override.get(item_name, None) or \
self._data_source.get(item_name, default)
def get_raw(self, item_name, default=None):
return self._override.get_raw(item_name, None) or \
self._data_source.get(item_name, default)
def get_boolean(self, item_name, default=None):
return self._override.get_boolean(item_name, None) or \
self._data_source.get_boolean(item_name, default)
class ConfigSectionInterface(BaseConfigSectionInterface):
def __init__(self, config_file_path=None, section_name=None):
section_name = (section_name or
getattr(self, 'SECTION_NAME', None) or
getattr(self, 'CONFIG_SECTION_NAME', None))
config_file_path = config_file_path or _get_path_from_env(
'CAFE_CONFIG_FILE_PATH')
super(ConfigSectionInterface, self).__init__(
config_file_path, section_name)