Initial directory skeleton
Implements: blueprint initial-skeleton Change-Id: If6559f5e1adf4aee70f5d92b5dfcf9fead86b205
This commit is contained in:
parent
507ff77b29
commit
74eee09e4b
@ -1,4 +1,5 @@
|
||||
[DEFAULT]
|
||||
test_command=${PYTHON:-python} -m subunit.run discover octavia $LISTOPT $IDOPTION
|
||||
#test_command=${PYTHON:-python} -m subunit.run discover octavia $LISTOPT $IDOPTION
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_LOG_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./octavia/tests} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
|
0
client/__init__.py
Normal file
0
client/__init__.py
Normal file
19
etc/octavia.conf
Normal file
19
etc/octavia.conf
Normal file
@ -0,0 +1,19 @@
|
||||
[DEFAULT]
|
||||
# Print more verbose output (set logging level to INFO instead of default WARNING level).
|
||||
# verbose = False
|
||||
# Print debugging output (set logging level to DEBUG instead of default WARNING level).
|
||||
# debug = False
|
||||
|
||||
[database]
|
||||
# This line MUST be changed to actually run the plugin.
|
||||
# Example:
|
||||
# connection = mysql://root:pass@127.0.0.1:3306/octavia
|
||||
# Replace 127.0.0.1 above with the IP address of the database used by the
|
||||
# main octavia server. (Leave it as is if the database runs on this host.)
|
||||
|
||||
# connection = sqlite://
|
||||
|
||||
# NOTE: In deployment the [database] section and its connection attribute may
|
||||
# be set in the corresponding core plugin '.ini' file. However, it is suggested
|
||||
# to put the [database] section and its connection attribute in this
|
||||
# configuration file.
|
@ -0,0 +1,19 @@
|
||||
# Copyright 2011-2014 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 gettext
|
||||
|
||||
|
||||
gettext.install('octavia', unicode=1)
|
0
octavia/amphorae/__init__.py
Normal file
0
octavia/amphorae/__init__.py
Normal file
0
octavia/amphorae/backends/__init__.py
Normal file
0
octavia/amphorae/backends/__init__.py
Normal file
0
octavia/amphorae/drivers/__init__.py
Normal file
0
octavia/amphorae/drivers/__init__.py
Normal file
0
octavia/amphorae/drivers/base.py
Normal file
0
octavia/amphorae/drivers/base.py
Normal file
0
octavia/amphorae/drivers/haproxy-simple/__init__.py
Normal file
0
octavia/amphorae/drivers/haproxy-simple/__init__.py
Normal file
0
octavia/common/__init__.py
Normal file
0
octavia/common/__init__.py
Normal file
135
octavia/common/config.py
Normal file
135
octavia/common/config.py
Normal file
@ -0,0 +1,135 @@
|
||||
# Copyright 2011 VMware, Inc., 2014 A10 Networks
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Routines for configuring Octavia
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import options as db_options
|
||||
from oslo import messaging
|
||||
# from paste import deploy
|
||||
|
||||
from octavia.common import utils
|
||||
from octavia.openstack.common import log as logging
|
||||
from octavia import version
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
core_opts = [
|
||||
cfg.StrOpt('bind_host', default='0.0.0.0',
|
||||
help=_("The host IP to bind to")),
|
||||
cfg.IntOpt('bind_port', default=9696,
|
||||
help=_("The port to bind to")),
|
||||
cfg.StrOpt('api_paste_config', default="api-paste.ini",
|
||||
help=_("The API paste config file to use")),
|
||||
cfg.StrOpt('api_extensions_path', default="",
|
||||
help=_("The path for API extensions")),
|
||||
cfg.StrOpt('auth_strategy', default='keystone',
|
||||
help=_("The type of authentication to use")),
|
||||
cfg.BoolOpt('allow_bulk', default=True,
|
||||
help=_("Allow the usage of the bulk API")),
|
||||
cfg.BoolOpt('allow_pagination', default=False,
|
||||
help=_("Allow the usage of the pagination")),
|
||||
cfg.BoolOpt('allow_sorting', default=False,
|
||||
help=_("Allow the usage of the sorting")),
|
||||
cfg.StrOpt('pagination_max_limit', default="-1",
|
||||
help=_("The maximum number of items returned in a single "
|
||||
"response, value was 'infinite' or negative integer "
|
||||
"means no limit")),
|
||||
cfg.StrOpt('host', default=utils.get_hostname(),
|
||||
help=_("The hostname Octavia is running on")),
|
||||
cfg.StrOpt('nova_url',
|
||||
default='http://127.0.0.1:8774/v2',
|
||||
help=_('URL for connection to nova')),
|
||||
cfg.StrOpt('nova_admin_username',
|
||||
help=_('Username for connecting to nova in admin context')),
|
||||
cfg.StrOpt('nova_admin_password',
|
||||
help=_('Password for connection to nova in admin context'),
|
||||
secret=True),
|
||||
cfg.StrOpt('nova_admin_tenant_id',
|
||||
help=_('The uuid of the admin nova tenant')),
|
||||
cfg.StrOpt('nova_admin_auth_url',
|
||||
default='http://localhost:5000/v2.0',
|
||||
help=_('Authorization URL for connecting to nova in admin '
|
||||
'context')),
|
||||
cfg.StrOpt('nova_ca_certificates_file',
|
||||
help=_('CA file for novaclient to verify server certificates')),
|
||||
cfg.BoolOpt('nova_api_insecure', default=False,
|
||||
help=_("If True, ignore any SSL validation issues")),
|
||||
cfg.StrOpt('nova_region_name',
|
||||
help=_('Name of nova region to use. Useful if keystone manages'
|
||||
' more than one region.')),
|
||||
]
|
||||
|
||||
core_cli_opts = []
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(core_opts)
|
||||
cfg.CONF.register_cli_opts(core_cli_opts)
|
||||
|
||||
# Ensure that the control exchange is set correctly
|
||||
messaging.set_transport_defaults(control_exchange='octavia')
|
||||
_SQL_CONNECTION_DEFAULT = 'sqlite://'
|
||||
# Update the default QueuePool parameters. These can be tweaked by the
|
||||
# configuration variables - max_pool_size, max_overflow and pool_timeout
|
||||
db_options.set_defaults(cfg.CONF,
|
||||
connection=_SQL_CONNECTION_DEFAULT,
|
||||
sqlite_db='', max_pool_size=10,
|
||||
max_overflow=20, pool_timeout=10)
|
||||
|
||||
|
||||
def init(args, **kwargs):
|
||||
cfg.CONF(args=args, project='octavia',
|
||||
version='%%prog %s' % version.version_info.release_string(),
|
||||
**kwargs)
|
||||
|
||||
|
||||
def setup_logging(conf):
|
||||
"""Sets up the logging options for a log with supplied name.
|
||||
|
||||
:param conf: a cfg.ConfOpts object
|
||||
"""
|
||||
product_name = "octavia"
|
||||
logging.setup(product_name)
|
||||
LOG.info(_("Logging enabled!"))
|
||||
|
||||
|
||||
# def load_paste_app(app_name):
|
||||
# """Builds and returns a WSGI app from a paste config file.
|
||||
|
||||
# :param app_name: Name of the application to load
|
||||
# :raises ConfigFilesNotFoundError when config file cannot be located
|
||||
# :raises RuntimeError when application cannot be loaded from config file
|
||||
# """
|
||||
|
||||
# config_path = cfg.CONF.find_file(cfg.CONF.api_paste_config)
|
||||
# if not config_path:
|
||||
# raise cfg.ConfigFilesNotFoundError(
|
||||
# config_files=[cfg.CONF.api_paste_config])
|
||||
# config_path = os.path.abspath(config_path)
|
||||
# LOG.info(_("Config paste file: %s"), config_path)
|
||||
|
||||
# try:
|
||||
# app = deploy.loadapp("config:%s" % config_path, name=app_name)
|
||||
# except (LookupError, ImportError):
|
||||
# msg = (_("Unable to load %(app_name)s from "
|
||||
# "configuration file %(config_path)s.") %
|
||||
# {'app_name': app_name,
|
||||
# 'config_path': config_path})
|
||||
# LOG.exception(msg)
|
||||
# raise RuntimeError(msg)
|
||||
# return app
|
28
octavia/common/constants.py
Normal file
28
octavia/common/constants.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2012-2014 OpenStack Foundation.
|
||||
#
|
||||
# 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.
|
||||
|
||||
LB_METHOD_ROUND_ROBIN = 'ROUND_ROBIN'
|
||||
LB_METHOD_LEAST_CONNECTIONS = 'LEAST_CONNECTIONS'
|
||||
LB_METHOD_SOURCE_IP = 'SOURCE_IP'
|
||||
|
||||
PROTOCOL_TCP = 'TCP'
|
||||
PROTOCOL_HTTP = 'HTTP'
|
||||
PROTOCOL_HTTPS = 'HTTPS'
|
||||
PROTOCOL_UDP = 'UDP'
|
||||
|
||||
HEALTH_MONITOR_PING = 'PING'
|
||||
HEALTH_MONITOR_TCP = 'TCP'
|
||||
HEALTH_MONITOR_HTTP = 'HTTP'
|
||||
HEALTH_MONITOR_HTTPS = 'HTTPS'
|
59
octavia/common/exceptions.py
Normal file
59
octavia/common/exceptions.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2011 VMware, Inc, 2014 A10 Networks
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Octavia base exception handling.
|
||||
"""
|
||||
|
||||
from octavia.openstack.common import excutils
|
||||
|
||||
|
||||
class OctaviaException(Exception):
|
||||
"""Base Octavia Exception.
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
a 'message' property. That message will get printf'd
|
||||
with the keyword arguments provided to the constructor.
|
||||
"""
|
||||
message = _("An unknown exception occurred.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
try:
|
||||
super(OctaviaException, self).__init__(self.message % kwargs)
|
||||
self.msg = self.message % kwargs
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if not self.use_fatal_exceptions():
|
||||
ctxt.reraise = False
|
||||
# at least get the core message out if something happened
|
||||
super(OctaviaException, self).__init__(self.message)
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.msg)
|
||||
|
||||
def use_fatal_exceptions(self):
|
||||
return False
|
||||
|
||||
|
||||
class BadRequest(OctaviaException):
|
||||
message = _('Bad %(resource)s request: %(msg)s')
|
||||
|
||||
|
||||
class NotFound(OctaviaException):
|
||||
pass
|
||||
|
||||
|
||||
class NotAuthorized(OctaviaException):
|
||||
message = _("Not authorized.")
|
76
octavia/common/utils.py
Normal file
76
octavia/common/utils.py
Normal file
@ -0,0 +1,76 @@
|
||||
# Copyright 2011, VMware, Inc., 2014 A10 Networks
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Borrowed from nova code base, more utilities will be added/borrowed as and
|
||||
# when needed.
|
||||
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
import random
|
||||
import socket
|
||||
|
||||
# from eventlet.green import subprocess
|
||||
# from oslo.config import cfg
|
||||
|
||||
from octavia.openstack.common import excutils
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_hostname():
|
||||
return socket.gethostname()
|
||||
|
||||
|
||||
def get_random_string(length):
|
||||
"""Get a random hex string of the specified length.
|
||||
|
||||
based on Cinder library
|
||||
cinder/transfer/api.py
|
||||
"""
|
||||
rndstr = ""
|
||||
random.seed(datetime.datetime.now().microsecond)
|
||||
while len(rndstr) < length:
|
||||
rndstr += hashlib.sha224(str(random.random())).hexdigest()
|
||||
|
||||
return rndstr[0:length]
|
||||
|
||||
|
||||
class exception_logger(object):
|
||||
"""Wrap a function and log raised exception
|
||||
|
||||
:param logger: the logger to log the exception default is LOG.exception
|
||||
|
||||
:returns: origin value if no exception raised; re-raise the exception if
|
||||
any occurred
|
||||
|
||||
"""
|
||||
def __init__(self, logger=None):
|
||||
self.logger = logger
|
||||
|
||||
def __call__(self, func):
|
||||
if self.logger is None:
|
||||
LOG = logging.getLogger(func.__module__)
|
||||
self.logger = LOG.exception
|
||||
|
||||
def call(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.logger(e)
|
||||
return call
|
0
octavia/controller/__init__.py
Normal file
0
octavia/controller/__init__.py
Normal file
0
octavia/db/__init__.py
Normal file
0
octavia/db/__init__.py
Normal file
0
octavia/db/migration/__init__.py
Normal file
0
octavia/db/migration/__init__.py
Normal file
0
octavia/network/__init__.py
Normal file
0
octavia/network/__init__.py
Normal file
0
octavia/network/base.py
Normal file
0
octavia/network/base.py
Normal file
0
octavia/network/drivers/__init__.py
Normal file
0
octavia/network/drivers/__init__.py
Normal file
3
octavia/network/drivers/neutron/README
Normal file
3
octavia/network/drivers/neutron/README
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
This is the only module in all of octavia that is allowed to import neutron directly.
|
||||
|
0
octavia/network/drivers/neutron/__init__.py
Normal file
0
octavia/network/drivers/neutron/__init__.py
Normal file
0
octavia/network/drivers/nova-network/__init__.py
Normal file
0
octavia/network/drivers/nova-network/__init__.py
Normal file
0
octavia/openstack/__init__.py
Normal file
0
octavia/openstack/__init__.py
Normal file
17
octavia/openstack/common/__init__.py
Normal file
17
octavia/openstack/common/__init__.py
Normal file
@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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 six
|
||||
|
||||
|
||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
0
octavia/openstack/common/cache/__init__.py
vendored
Normal file
0
octavia/openstack/common/cache/__init__.py
vendored
Normal file
250
octavia/openstack/common/cache/backends.py
vendored
Normal file
250
octavia/openstack/common/cache/backends.py
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# 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 six
|
||||
|
||||
|
||||
NOTSET = object()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseCache(object):
|
||||
"""Base Cache Abstraction
|
||||
|
||||
:params parsed_url: Parsed url object.
|
||||
:params options: A dictionary with configuration parameters
|
||||
for the cache. For example:
|
||||
|
||||
- default_ttl: An integer defining the default ttl for keys.
|
||||
"""
|
||||
|
||||
def __init__(self, parsed_url, options=None):
|
||||
self._parsed_url = parsed_url
|
||||
self._options = options or {}
|
||||
self._default_ttl = int(self._options.get('default_ttl', 0))
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set(self, key, value, ttl, not_exists=False):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
def set(self, key, value, ttl, not_exists=False):
|
||||
"""Sets or updates a cache entry
|
||||
|
||||
.. note:: Thread-safety is required and has to be guaranteed by the
|
||||
backend implementation.
|
||||
|
||||
:params key: Item key as string.
|
||||
:type key: `unicode string`
|
||||
:params value: Value to assign to the key. This can be anything that
|
||||
is handled by the current backend.
|
||||
:params ttl: Key's timeout in seconds. 0 means no timeout.
|
||||
:type ttl: int
|
||||
:params not_exists: If True, the key will be set if it doesn't exist.
|
||||
Otherwise, it'll always be set.
|
||||
:type not_exists: bool
|
||||
|
||||
:returns: True if the operation succeeds, False otherwise.
|
||||
"""
|
||||
if ttl is None:
|
||||
ttl = self._default_ttl
|
||||
|
||||
return self._set(key, value, ttl, not_exists)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.set(key, value, self._default_ttl)
|
||||
|
||||
def setdefault(self, key, value):
|
||||
"""Sets the key value to `value` if it doesn't exist
|
||||
|
||||
:params key: Item key as string.
|
||||
:type key: `unicode string`
|
||||
:params value: Value to assign to the key. This can be anything that
|
||||
is handled by the current backend.
|
||||
"""
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = value
|
||||
return value
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get(self, key, default):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Gets one item from the cache
|
||||
|
||||
.. note:: Thread-safety is required and it has to be guaranteed
|
||||
by the backend implementation.
|
||||
|
||||
:params key: Key for the item to retrieve from the cache.
|
||||
:params default: The default value to return.
|
||||
|
||||
:returns: `key`'s value in the cache if it exists, otherwise
|
||||
`default` should be returned.
|
||||
"""
|
||||
return self._get(key, default)
|
||||
|
||||
def __getitem__(self, key):
|
||||
value = self.get(key, NOTSET)
|
||||
|
||||
if value is NOTSET:
|
||||
raise KeyError
|
||||
|
||||
return value
|
||||
|
||||
@abc.abstractmethod
|
||||
def __delitem__(self, key):
|
||||
"""Removes an item from cache.
|
||||
|
||||
.. note:: Thread-safety is required and it has to be guaranteed by
|
||||
the backend implementation.
|
||||
|
||||
:params key: The key to remove.
|
||||
|
||||
:returns: The key value if there's one
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def _clear(self):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
def clear(self):
|
||||
"""Removes all items from the cache.
|
||||
|
||||
.. note:: Thread-safety is required and it has to be guaranteed by
|
||||
the backend implementation.
|
||||
"""
|
||||
return self._clear()
|
||||
|
||||
@abc.abstractmethod
|
||||
def _incr(self, key, delta):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
def incr(self, key, delta=1):
|
||||
"""Increments the value for a key
|
||||
|
||||
:params key: The key for the value to be incremented
|
||||
:params delta: Number of units by which to increment the value.
|
||||
Pass a negative number to decrement the value.
|
||||
|
||||
:returns: The new value
|
||||
"""
|
||||
return self._incr(key, delta)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _append_tail(self, key, tail):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
def append_tail(self, key, tail):
|
||||
"""Appends `tail` to `key`'s value.
|
||||
|
||||
:params key: The key of the value to which `tail` should be appended.
|
||||
:params tail: The list of values to append to the original.
|
||||
|
||||
:returns: The new value
|
||||
"""
|
||||
|
||||
if not hasattr(tail, "__iter__"):
|
||||
raise TypeError('Tail must be an iterable')
|
||||
|
||||
if not isinstance(tail, list):
|
||||
# NOTE(flaper87): Make sure we pass a list
|
||||
# down to the implementation. Not all drivers
|
||||
# have support for generators, sets or other
|
||||
# iterables.
|
||||
tail = list(tail)
|
||||
|
||||
return self._append_tail(key, tail)
|
||||
|
||||
def append(self, key, value):
|
||||
"""Appends `value` to `key`'s value.
|
||||
|
||||
:params key: The key of the value to which `tail` should be appended.
|
||||
:params value: The value to append to the original.
|
||||
|
||||
:returns: The new value
|
||||
"""
|
||||
return self.append_tail(key, [value])
|
||||
|
||||
@abc.abstractmethod
|
||||
def __contains__(self, key):
|
||||
"""Verifies that a key exists.
|
||||
|
||||
:params key: The key to verify.
|
||||
|
||||
:returns: True if the key exists, otherwise False.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_many(self, keys, default):
|
||||
"""Implementations of this class have to override this method."""
|
||||
return ((k, self.get(k, default=default)) for k in keys)
|
||||
|
||||
def get_many(self, keys, default=NOTSET):
|
||||
"""Gets keys' value from cache
|
||||
|
||||
:params keys: List of keys to retrieve.
|
||||
:params default: The default value to return for each key that is not
|
||||
in the cache.
|
||||
|
||||
:returns: A generator of (key, value)
|
||||
"""
|
||||
return self._get_many(keys, default)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_many(self, data, ttl):
|
||||
"""Implementations of this class have to override this method."""
|
||||
|
||||
for key, value in data.items():
|
||||
self.set(key, value, ttl=ttl)
|
||||
|
||||
def set_many(self, data, ttl=None):
|
||||
"""Puts several items into the cache at once
|
||||
|
||||
Depending on the backend, this operation may or may not be efficient.
|
||||
The default implementation calls set for each (key, value) pair
|
||||
passed, other backends support set_many operations as part of their
|
||||
protocols.
|
||||
|
||||
:params data: A dictionary like {key: val} to store in the cache.
|
||||
:params ttl: Key's timeout in seconds.
|
||||
"""
|
||||
|
||||
if ttl is None:
|
||||
ttl = self._default_ttl
|
||||
|
||||
self._set_many(data, ttl)
|
||||
|
||||
def update(self, **kwargs):
|
||||
"""Sets several (key, value) paris.
|
||||
|
||||
Refer to the `set_many` docstring.
|
||||
"""
|
||||
self.set_many(kwargs, ttl=self._default_ttl)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _unset_many(self, keys):
|
||||
"""Implementations of this class have to override this method."""
|
||||
for key in keys:
|
||||
del self[key]
|
||||
|
||||
def unset_many(self, keys):
|
||||
"""Removes several keys from the cache at once
|
||||
|
||||
:params keys: List of keys to unset.
|
||||
"""
|
||||
self._unset_many(keys)
|
78
octavia/openstack/common/cache/cache.py
vendored
Normal file
78
octavia/openstack/common/cache/cache.py
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Cache library.
|
||||
|
||||
Supported configuration options:
|
||||
|
||||
`default_backend`: Name of the cache backend to use.
|
||||
`key_namespace`: Namespace under which keys will be created.
|
||||
"""
|
||||
|
||||
from six.moves.urllib import parse
|
||||
from stevedore import driver
|
||||
|
||||
|
||||
def _get_olso_configs():
|
||||
"""Returns the oslo.config options to register."""
|
||||
# NOTE(flaper87): Oslo config should be
|
||||
# optional. Instead of doing try / except
|
||||
# at the top of this file, lets import cfg
|
||||
# here and assume that the caller of this
|
||||
# function already took care of this dependency.
|
||||
from oslo.config import cfg
|
||||
|
||||
return [
|
||||
cfg.StrOpt('cache_url', default='memory://',
|
||||
help='URL to connect to the cache back end.')
|
||||
]
|
||||
|
||||
|
||||
def register_oslo.configs(conf):
|
||||
"""Registers a cache configuration options
|
||||
|
||||
:params conf: Config object.
|
||||
:type conf: `cfg.ConfigOptions`
|
||||
"""
|
||||
conf.register_opts(_get_olso_configs())
|
||||
|
||||
|
||||
def get_cache(url='memory://'):
|
||||
"""Loads the cache backend
|
||||
|
||||
This function loads the cache backend
|
||||
specified in the given configuration.
|
||||
|
||||
:param conf: Configuration instance to use
|
||||
"""
|
||||
|
||||
parsed = parse.urlparse(url)
|
||||
backend = parsed.scheme
|
||||
|
||||
query = parsed.query
|
||||
# NOTE(flaper87): We need the following hack
|
||||
# for python versions < 2.7.5. Previous versions
|
||||
# of python parsed query params just for 'known'
|
||||
# schemes. This was changed in this patch:
|
||||
# http://hg.python.org/cpython/rev/79e6ff3d9afd
|
||||
if not query and '?' in parsed.path:
|
||||
query = parsed.path.split('?', 1)[-1]
|
||||
parameters = parse.parse_qsl(query)
|
||||
kwargs = {'options': dict(parameters)}
|
||||
|
||||
mgr = driver.DriverManager('octavia.openstack.common.cache.backends', backend,
|
||||
invoke_on_load=True,
|
||||
invoke_args=[parsed],
|
||||
invoke_kwds=kwargs)
|
||||
return mgr.driver
|
126
octavia/openstack/common/context.py
Normal file
126
octavia/openstack/common/context.py
Normal file
@ -0,0 +1,126 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Simple class that stores security context information in the web request.
|
||||
|
||||
Projects should subclass this class if they wish to enhance the request
|
||||
context or provide additional information in their specific WSGI pipeline.
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import uuid
|
||||
|
||||
|
||||
def generate_request_id():
|
||||
return b'req-' + str(uuid.uuid4()).encode('ascii')
|
||||
|
||||
|
||||
class RequestContext(object):
|
||||
|
||||
"""Helper class to represent useful information about a request context.
|
||||
|
||||
Stores information about the security context under which the user
|
||||
accesses the system, as well as additional request information.
|
||||
"""
|
||||
|
||||
user_idt_format = '{user} {tenant} {domain} {user_domain} {p_domain}'
|
||||
|
||||
def __init__(self, auth_token=None, user=None, tenant=None, domain=None,
|
||||
user_domain=None, project_domain=None, is_admin=False,
|
||||
read_only=False, show_deleted=False, request_id=None,
|
||||
instance_uuid=None):
|
||||
self.auth_token = auth_token
|
||||
self.user = user
|
||||
self.tenant = tenant
|
||||
self.domain = domain
|
||||
self.user_domain = user_domain
|
||||
self.project_domain = project_domain
|
||||
self.is_admin = is_admin
|
||||
self.read_only = read_only
|
||||
self.show_deleted = show_deleted
|
||||
self.instance_uuid = instance_uuid
|
||||
if not request_id:
|
||||
request_id = generate_request_id()
|
||||
self.request_id = request_id
|
||||
|
||||
def to_dict(self):
|
||||
user_idt = (
|
||||
self.user_idt_format.format(user=self.user or '-',
|
||||
tenant=self.tenant or '-',
|
||||
domain=self.domain or '-',
|
||||
user_domain=self.user_domain or '-',
|
||||
p_domain=self.project_domain or '-'))
|
||||
|
||||
return {'user': self.user,
|
||||
'tenant': self.tenant,
|
||||
'domain': self.domain,
|
||||
'user_domain': self.user_domain,
|
||||
'project_domain': self.project_domain,
|
||||
'is_admin': self.is_admin,
|
||||
'read_only': self.read_only,
|
||||
'show_deleted': self.show_deleted,
|
||||
'auth_token': self.auth_token,
|
||||
'request_id': self.request_id,
|
||||
'instance_uuid': self.instance_uuid,
|
||||
'user_identity': user_idt}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, ctx):
|
||||
return cls(
|
||||
auth_token=ctx.get("auth_token"),
|
||||
user=ctx.get("user"),
|
||||
tenant=ctx.get("tenant"),
|
||||
domain=ctx.get("domain"),
|
||||
user_domain=ctx.get("user_domain"),
|
||||
project_domain=ctx.get("project_domain"),
|
||||
is_admin=ctx.get("is_admin", False),
|
||||
read_only=ctx.get("read_only", False),
|
||||
show_deleted=ctx.get("show_deleted", False),
|
||||
request_id=ctx.get("request_id"),
|
||||
instance_uuid=ctx.get("instance_uuid"))
|
||||
|
||||
|
||||
def get_admin_context(show_deleted=False):
|
||||
context = RequestContext(None,
|
||||
tenant=None,
|
||||
is_admin=True,
|
||||
show_deleted=show_deleted)
|
||||
return context
|
||||
|
||||
|
||||
def get_context_from_function_and_args(function, args, kwargs):
|
||||
"""Find an arg of type RequestContext and return it.
|
||||
|
||||
This is useful in a couple of decorators where we don't
|
||||
know much about the function we're wrapping.
|
||||
"""
|
||||
|
||||
for arg in itertools.chain(kwargs.values(), args):
|
||||
if isinstance(arg, RequestContext):
|
||||
return arg
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_user_context(context):
|
||||
"""Indicates if the request context is a normal user."""
|
||||
if not context:
|
||||
return False
|
||||
if context.is_admin:
|
||||
return False
|
||||
if not context.user_id or not context.project_id:
|
||||
return False
|
||||
return True
|
145
octavia/openstack/common/eventlet_backdoor.py
Normal file
145
octavia/openstack/common/eventlet_backdoor.py
Normal file
@ -0,0 +1,145 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import errno
|
||||
import gc
|
||||
import os
|
||||
import pprint
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import eventlet
|
||||
import eventlet.backdoor
|
||||
import greenlet
|
||||
from oslo.config import cfg
|
||||
|
||||
from octavia.openstack.common.gettextutils import _LI
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
help_for_backdoor_port = (
|
||||
"Acceptable values are 0, <port>, and <start>:<end>, where 0 results "
|
||||
"in listening on a random tcp port number; <port> results in listening "
|
||||
"on the specified port number (and not enabling backdoor if that port "
|
||||
"is in use); and <start>:<end> results in listening on the smallest "
|
||||
"unused port number within the specified range of port numbers. The "
|
||||
"chosen port is displayed in the service's log file.")
|
||||
eventlet_backdoor_opts = [
|
||||
cfg.StrOpt('backdoor_port',
|
||||
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(eventlet_backdoor_opts)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EventletBackdoorConfigValueError(Exception):
|
||||
def __init__(self, port_range, help_msg, ex):
|
||||
msg = ('Invalid backdoor_port configuration %(range)s: %(ex)s. '
|
||||
'%(help)s' %
|
||||
{'range': port_range, 'ex': ex, 'help': help_msg})
|
||||
super(EventletBackdoorConfigValueError, self).__init__(msg)
|
||||
self.port_range = port_range
|
||||
|
||||
|
||||
def _dont_use_this():
|
||||
print("Don't use this, just disconnect instead")
|
||||
|
||||
|
||||
def _find_objects(t):
|
||||
return [o for o in gc.get_objects() if isinstance(o, t)]
|
||||
|
||||
|
||||
def _print_greenthreads():
|
||||
for i, gt in enumerate(_find_objects(greenlet.greenlet)):
|
||||
print(i, gt)
|
||||
traceback.print_stack(gt.gr_frame)
|
||||
print()
|
||||
|
||||
|
||||
def _print_nativethreads():
|
||||
for threadId, stack in sys._current_frames().items():
|
||||
print(threadId)
|
||||
traceback.print_stack(stack)
|
||||
print()
|
||||
|
||||
|
||||
def _parse_port_range(port_range):
|
||||
if ':' not in port_range:
|
||||
start, end = port_range, port_range
|
||||
else:
|
||||
start, end = port_range.split(':', 1)
|
||||
try:
|
||||
start, end = int(start), int(end)
|
||||
if end < start:
|
||||
raise ValueError
|
||||
return start, end
|
||||
except ValueError as ex:
|
||||
raise EventletBackdoorConfigValueError(port_range, ex,
|
||||
help_for_backdoor_port)
|
||||
|
||||
|
||||
def _listen(host, start_port, end_port, listen_func):
|
||||
try_port = start_port
|
||||
while True:
|
||||
try:
|
||||
return listen_func((host, try_port))
|
||||
except socket.error as exc:
|
||||
if (exc.errno != errno.EADDRINUSE or
|
||||
try_port >= end_port):
|
||||
raise
|
||||
try_port += 1
|
||||
|
||||
|
||||
def initialize_if_enabled():
|
||||
backdoor_locals = {
|
||||
'exit': _dont_use_this, # So we don't exit the entire process
|
||||
'quit': _dont_use_this, # So we don't exit the entire process
|
||||
'fo': _find_objects,
|
||||
'pgt': _print_greenthreads,
|
||||
'pnt': _print_nativethreads,
|
||||
}
|
||||
|
||||
if CONF.backdoor_port is None:
|
||||
return None
|
||||
|
||||
start_port, end_port = _parse_port_range(str(CONF.backdoor_port))
|
||||
|
||||
# NOTE(johannes): The standard sys.displayhook will print the value of
|
||||
# the last expression and set it to __builtin__._, which overwrites
|
||||
# the __builtin__._ that gettext sets. Let's switch to using pprint
|
||||
# since it won't interact poorly with gettext, and it's easier to
|
||||
# read the output too.
|
||||
def displayhook(val):
|
||||
if val is not None:
|
||||
pprint.pprint(val)
|
||||
sys.displayhook = displayhook
|
||||
|
||||
sock = _listen('localhost', start_port, end_port, eventlet.listen)
|
||||
|
||||
# In the case of backdoor port being zero, a port number is assigned by
|
||||
# listen(). In any case, pull the port number out here.
|
||||
port = sock.getsockname()[1]
|
||||
LOG.info(
|
||||
_LI('Eventlet backdoor listening on %(port)s for process %(pid)d') %
|
||||
{'port': port, 'pid': os.getpid()}
|
||||
)
|
||||
eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock,
|
||||
locals=backdoor_locals)
|
||||
return port
|
113
octavia/openstack/common/excutils.py
Normal file
113
octavia/openstack/common/excutils.py
Normal file
@ -0,0 +1,113 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# Copyright 2012, Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Exception related utilities.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import six
|
||||
|
||||
from octavia.openstack.common.gettextutils import _LE
|
||||
|
||||
|
||||
class save_and_reraise_exception(object):
|
||||
"""Save current exception, run some code and then re-raise.
|
||||
|
||||
In some cases the exception context can be cleared, resulting in None
|
||||
being attempted to be re-raised after an exception handler is run. This
|
||||
can happen when eventlet switches greenthreads or when running an
|
||||
exception handler, code raises and catches an exception. In both
|
||||
cases the exception context will be cleared.
|
||||
|
||||
To work around this, we save the exception state, run handler code, and
|
||||
then re-raise the original exception. If another exception occurs, the
|
||||
saved exception is logged and the new exception is re-raised.
|
||||
|
||||
In some cases the caller may not want to re-raise the exception, and
|
||||
for those circumstances this context provides a reraise flag that
|
||||
can be used to suppress the exception. For example::
|
||||
|
||||
except Exception:
|
||||
with save_and_reraise_exception() as ctxt:
|
||||
decide_if_need_reraise()
|
||||
if not should_be_reraised:
|
||||
ctxt.reraise = False
|
||||
|
||||
If another exception occurs and reraise flag is False,
|
||||
the saved exception will not be logged.
|
||||
|
||||
If the caller wants to raise new exception during exception handling
|
||||
he/she sets reraise to False initially with an ability to set it back to
|
||||
True if needed::
|
||||
|
||||
except Exception:
|
||||
with save_and_reraise_exception(reraise=False) as ctxt:
|
||||
[if statements to determine whether to raise a new exception]
|
||||
# Not raising a new exception, so reraise
|
||||
ctxt.reraise = True
|
||||
"""
|
||||
def __init__(self, reraise=True):
|
||||
self.reraise = reraise
|
||||
|
||||
def __enter__(self):
|
||||
self.type_, self.value, self.tb, = sys.exc_info()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is not None:
|
||||
if self.reraise:
|
||||
logging.error(_LE('Original exception being dropped: %s'),
|
||||
traceback.format_exception(self.type_,
|
||||
self.value,
|
||||
self.tb))
|
||||
return False
|
||||
if self.reraise:
|
||||
six.reraise(self.type_, self.value, self.tb)
|
||||
|
||||
|
||||
def forever_retry_uncaught_exceptions(infunc):
|
||||
def inner_func(*args, **kwargs):
|
||||
last_log_time = 0
|
||||
last_exc_message = None
|
||||
exc_count = 0
|
||||
while True:
|
||||
try:
|
||||
return infunc(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
this_exc_message = six.u(str(exc))
|
||||
if this_exc_message == last_exc_message:
|
||||
exc_count += 1
|
||||
else:
|
||||
exc_count = 1
|
||||
# Do not log any more frequently than once a minute unless
|
||||
# the exception message changes
|
||||
cur_time = int(time.time())
|
||||
if (cur_time - last_log_time > 60 or
|
||||
this_exc_message != last_exc_message):
|
||||
logging.exception(
|
||||
_LE('Unexpected exception occurred %d time(s)... '
|
||||
'retrying.') % exc_count)
|
||||
last_log_time = cur_time
|
||||
last_exc_message = this_exc_message
|
||||
exc_count = 0
|
||||
# This should be a very rare event. In case it isn't, do
|
||||
# a sleep.
|
||||
time.sleep(1)
|
||||
return inner_func
|
146
octavia/openstack/common/fileutils.py
Normal file
146
octavia/openstack/common/fileutils.py
Normal file
@ -0,0 +1,146 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 contextlib
|
||||
import errno
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from octavia.openstack.common import excutils
|
||||
from octavia.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_FILE_CACHE = {}
|
||||
|
||||
|
||||
def ensure_tree(path):
|
||||
"""Create a directory (and any ancestor directories required)
|
||||
|
||||
:param path: Directory to create
|
||||
"""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
if not os.path.isdir(path):
|
||||
raise
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def read_cached_file(filename, force_reload=False):
|
||||
"""Read from a file if it has been modified.
|
||||
|
||||
:param force_reload: Whether to reload the file.
|
||||
:returns: A tuple with a boolean specifying if the data is fresh
|
||||
or not.
|
||||
"""
|
||||
global _FILE_CACHE
|
||||
|
||||
if force_reload:
|
||||
delete_cached_file(filename)
|
||||
|
||||
reloaded = False
|
||||
mtime = os.path.getmtime(filename)
|
||||
cache_info = _FILE_CACHE.setdefault(filename, {})
|
||||
|
||||
if not cache_info or mtime > cache_info.get('mtime', 0):
|
||||
LOG.debug("Reloading cached file %s" % filename)
|
||||
with open(filename) as fap:
|
||||
cache_info['data'] = fap.read()
|
||||
cache_info['mtime'] = mtime
|
||||
reloaded = True
|
||||
return (reloaded, cache_info['data'])
|
||||
|
||||
|
||||
def delete_cached_file(filename):
|
||||
"""Delete cached file if present.
|
||||
|
||||
:param filename: filename to delete
|
||||
"""
|
||||
global _FILE_CACHE
|
||||
|
||||
if filename in _FILE_CACHE:
|
||||
del _FILE_CACHE[filename]
|
||||
|
||||
|
||||
def delete_if_exists(path, remove=os.unlink):
|
||||
"""Delete a file, but ignore file not found error.
|
||||
|
||||
:param path: File to delete
|
||||
:param remove: Optional function to remove passed path
|
||||
"""
|
||||
|
||||
try:
|
||||
remove(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def remove_path_on_error(path, remove=delete_if_exists):
|
||||
"""Protect code that wants to operate on PATH atomically.
|
||||
Any exception will cause PATH to be removed.
|
||||
|
||||
:param path: File to work with
|
||||
:param remove: Optional function to remove passed path
|
||||
"""
|
||||
|
||||
try:
|
||||
yield
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
remove(path)
|
||||
|
||||
|
||||
def file_open(*args, **kwargs):
|
||||
"""Open file
|
||||
|
||||
see built-in open() documentation for more details
|
||||
|
||||
Note: The reason this is kept in a separate module is to easily
|
||||
be able to provide a stub module that doesn't alter system
|
||||
state at all (for unit tests)
|
||||
"""
|
||||
return open(*args, **kwargs)
|
||||
|
||||
|
||||
def write_to_tempfile(content, path=None, suffix='', prefix='tmp'):
|
||||
"""Create temporary file or use existing file.
|
||||
|
||||
This util is needed for creating temporary file with
|
||||
specified content, suffix and prefix. If path is not None,
|
||||
it will be used for writing content. If the path doesn't
|
||||
exist it'll be created.
|
||||
|
||||
:param content: content for temporary file.
|
||||
:param path: same as parameter 'dir' for mkstemp
|
||||
:param suffix: same as parameter 'suffix' for mkstemp
|
||||
:param prefix: same as parameter 'prefix' for mkstemp
|
||||
|
||||
For example: it can be used in database tests for creating
|
||||
configuration files.
|
||||
"""
|
||||
if path:
|
||||
ensure_tree(path)
|
||||
|
||||
(fd, path) = tempfile.mkstemp(suffix=suffix, dir=path, prefix=prefix)
|
||||
try:
|
||||
os.write(fd, content)
|
||||
finally:
|
||||
os.close(fd)
|
||||
return path
|
0
octavia/openstack/common/fixture/__init__.py
Normal file
0
octavia/openstack/common/fixture/__init__.py
Normal file
85
octavia/openstack/common/fixture/config.py
Normal file
85
octavia/openstack/common/fixture/config.py
Normal file
@ -0,0 +1,85 @@
|
||||
#
|
||||
# Copyright 2013 Mirantis, Inc.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 fixtures
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
|
||||
class Config(fixtures.Fixture):
|
||||
"""Allows overriding configuration settings for the test.
|
||||
|
||||
`conf` will be reset on cleanup.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, conf=cfg.CONF):
|
||||
self.conf = conf
|
||||
|
||||
def setUp(self):
|
||||
super(Config, self).setUp()
|
||||
# NOTE(morganfainberg): unregister must be added to cleanup before
|
||||
# reset is because cleanup works in reverse order of registered items,
|
||||
# and a reset must occur before unregistering options can occur.
|
||||
self.addCleanup(self._unregister_config_opts)
|
||||
self.addCleanup(self.conf.reset)
|
||||
self._registered_config_opts = {}
|
||||
|
||||
def config(self, **kw):
|
||||
"""Override configuration values.
|
||||
|
||||
The keyword arguments are the names of configuration options to
|
||||
override and their values.
|
||||
|
||||
If a `group` argument is supplied, the overrides are applied to
|
||||
the specified configuration option group, otherwise the overrides
|
||||
are applied to the ``default`` group.
|
||||
|
||||
"""
|
||||
|
||||
group = kw.pop('group', None)
|
||||
for k, v in six.iteritems(kw):
|
||||
self.conf.set_override(k, v, group)
|
||||
|
||||
def _unregister_config_opts(self):
|
||||
for group in self._registered_config_opts:
|
||||
self.conf.unregister_opts(self._registered_config_opts[group],
|
||||
group=group)
|
||||
|
||||
def register_opt(self, opt, group=None):
|
||||
"""Register a single option for the test run.
|
||||
|
||||
Options registered in this manner will automatically be unregistered
|
||||
during cleanup.
|
||||
|
||||
If a `group` argument is supplied, it will register the new option
|
||||
to that group, otherwise the option is registered to the ``default``
|
||||
group.
|
||||
"""
|
||||
self.conf.register_opt(opt, group=group)
|
||||
self._registered_config_opts.setdefault(group, set()).add(opt)
|
||||
|
||||
def register_opts(self, opts, group=None):
|
||||
"""Register multiple options for the test run.
|
||||
|
||||
This works in the same manner as register_opt() but takes a list of
|
||||
options as the first argument. All arguments will be registered to the
|
||||
same group if the ``group`` argument is supplied, otherwise all options
|
||||
will be registered to the ``default`` group.
|
||||
"""
|
||||
for opt in opts:
|
||||
self.register_opt(opt, group=group)
|
51
octavia/openstack/common/fixture/lockutils.py
Normal file
51
octavia/openstack/common/fixture/lockutils.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 fixtures
|
||||
|
||||
from octavia.openstack.common import lockutils
|
||||
|
||||
|
||||
class LockFixture(fixtures.Fixture):
|
||||
"""External locking fixture.
|
||||
|
||||
This fixture is basically an alternative to the synchronized decorator with
|
||||
the external flag so that tearDowns and addCleanups will be included in
|
||||
the lock context for locking between tests. The fixture is recommended to
|
||||
be the first line in a test method, like so::
|
||||
|
||||
def test_method(self):
|
||||
self.useFixture(LockFixture)
|
||||
...
|
||||
|
||||
or the first line in setUp if all the test methods in the class are
|
||||
required to be serialized. Something like::
|
||||
|
||||
class TestCase(testtools.testcase):
|
||||
def setUp(self):
|
||||
self.useFixture(LockFixture)
|
||||
super(TestCase, self).setUp()
|
||||
...
|
||||
|
||||
This is because addCleanups are put on a LIFO queue that gets run after the
|
||||
test method exits. (either by completing or raising an exception)
|
||||
"""
|
||||
def __init__(self, name, lock_file_prefix=None):
|
||||
self.mgr = lockutils.lock(name, lock_file_prefix, True)
|
||||
|
||||
def setUp(self):
|
||||
super(LockFixture, self).setUp()
|
||||
self.addCleanup(self.mgr.__exit__, None, None, None)
|
||||
self.lock = self.mgr.__enter__()
|
34
octavia/openstack/common/fixture/logging.py
Normal file
34
octavia/openstack/common/fixture/logging.py
Normal file
@ -0,0 +1,34 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||