Merge "Added support for oslo.config"
This commit is contained in:
commit
04fc974ee3
3
etc/os-config-generator/valence.conf
Normal file
3
etc/os-config-generator/valence.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
output_file = etc/valence/valence.conf.sample
|
||||
namespace = valence.conf
|
@ -1,30 +1,97 @@
|
||||
[DEFAULT]
|
||||
#LOG Levels - debug, info, warning, error, critical
|
||||
log_level= debug
|
||||
|
||||
#Server log settings
|
||||
debug=False
|
||||
|
||||
# Log to this file. Make sure the user running rsc has
|
||||
# permissions to write to this file!
|
||||
log_file=/var/log/valence/valence.log
|
||||
[api]
|
||||
|
||||
#address and port the server binds too
|
||||
bind_host = 0.0.0.0
|
||||
bind_port = 8181
|
||||
#
|
||||
# From valence.conf
|
||||
#
|
||||
|
||||
#Server request timeout
|
||||
timeout=1000
|
||||
# The port for the valence API server. (port value)
|
||||
# Minimum value: 0
|
||||
# Maximum value: 65535
|
||||
#bind_port = 8181
|
||||
|
||||
# The listen IP for the valence API server. (IP address value)
|
||||
#bind_host = 127.0.0.1
|
||||
|
||||
# Enable the integrated stand-alone API to service requests via HTTPS
|
||||
# instead of HTTP. If there is a front-end service performing HTTPS
|
||||
# offloading from the service, this option should be False; note, you
|
||||
# will want to change public API endpoint to represent SSL termination
|
||||
# URL with 'public_endpoint' option. (boolean value)
|
||||
#enable_ssl_api = false
|
||||
|
||||
# Number of workers for valence-api service. The default will be the
|
||||
# number of CPUs available. (integer value)
|
||||
#workers = 4
|
||||
|
||||
# The maximum timeout to wait for valence API server to come up.
|
||||
# (integer value)
|
||||
#timeout = 1000
|
||||
|
||||
# The maximum number of items returned in a single response from a
|
||||
# collection resource. (integer value)
|
||||
#max_limit = 1000
|
||||
|
||||
# Configuration file for WSGI definition of API. (string value)
|
||||
#api_paste_config = api-paste.ini
|
||||
|
||||
# Enable debug mode for valence-api service. (boolean value)
|
||||
#debug = false
|
||||
|
||||
# The log file location for valence-api service (string value)
|
||||
#log_file = /var/log/valence/valence.log
|
||||
|
||||
# The granularity of Error log outputs. (string value)
|
||||
#log_level = debug
|
||||
|
||||
# The log format. (string value)
|
||||
#log_format = %(asctime)s %(name)-4s %(levelname)-4s %(message)s
|
||||
|
||||
|
||||
[etcd]
|
||||
|
||||
#
|
||||
# From valence.conf
|
||||
#
|
||||
|
||||
# The port for the etcd server. (port value)
|
||||
# Minimum value: 0
|
||||
# Maximum value: 65535
|
||||
#port = 2379
|
||||
|
||||
# The listen IP for the etcd server. (IP address value)
|
||||
#host = 127.0.0.1
|
||||
|
||||
#Server workers
|
||||
workers=4
|
||||
|
||||
[podm]
|
||||
url=https://<ip address>:<port>
|
||||
user=<podm user>
|
||||
password=<podm admin>
|
||||
redfish_base_ext=/redfish/v1/
|
||||
|
||||
[database_etcd]
|
||||
host = localhost
|
||||
port = 2379
|
||||
#
|
||||
# From valence.conf
|
||||
#
|
||||
|
||||
# The URL of Redfish API server. (e.g.: http(s)://<IP>:<PORT>/).
|
||||
# Required. (string value)
|
||||
#url = <None>
|
||||
|
||||
# User account with admin/server-profile access privilege. Although
|
||||
# this property is not mandatory it's highly recommended to set a
|
||||
# username. Optional. (string value)
|
||||
#username = <None>
|
||||
|
||||
# User account password. Although this property is not mandatory, it's
|
||||
# highly recommended to set a password. Optional. (string value)
|
||||
#password = <None>
|
||||
|
||||
# Either a boolean value, a path to a CA_BUNDLE file or directory with
|
||||
# certificates of trusted CAs. If set to True the driver will verify
|
||||
# the host certificates; if False the driver will ignore verifying the
|
||||
# SSL certificate; If it's a path the driver will use the specified
|
||||
# certificate or one of the certificates in the directory. Defaults to
|
||||
# True. Optional. (boolean value)
|
||||
#verify_ca = <None>
|
||||
|
||||
# The URL extension that specifies the Redfish API version that
|
||||
# valence will interact with (string value)
|
||||
#base_ext = /redfish/v1/
|
||||
|
@ -35,7 +35,17 @@ if [ ! -d "/etc/valence" ]; then
|
||||
mkdir /etc/valence
|
||||
fi
|
||||
chown "$CURR_USER":"$CURR_USER" /etc/valence
|
||||
VALENCE_CONF=/etc/valence/valence.conf
|
||||
cp etc/valence/valence.conf.sample /etc/valence/valence.conf
|
||||
sudo sed -i "s/#debug\s*=.*/debug=true/" $VALENCE_CONF
|
||||
sudo sed -i "s/#log_level\s*=.*/log_level=debug/" $VALENCE_CONF
|
||||
sudo sed -i "s/#log_file\s*=.*/log_file=/var/log/valence/valence.log/" $VALENCE_CONF
|
||||
sudo sed -i "s/#bind_host\s*=.*/bind_host=0.0.0.0/" $VALENCE_CONF
|
||||
sudo sed -i "s/#bind_port\s*=.*/bind_port=8181/" $VALENCE_CONF
|
||||
sudo sed -i "s/#timeout\s*=.*/timeout=1000/" $VALENCE_CONF
|
||||
sudo sed -i "s/#workers\s*=.*/workers=4/" $VALENCE_CONF
|
||||
sudo sed -i "s/#host\s*=.*/host=localhost/" $VALENCE_CONF
|
||||
sudo sed -i "s/#port\s*=.*/port=2379/" $VALENCE_CONF
|
||||
|
||||
# create log directory for valence if it doesn't exist
|
||||
if [ ! -d "/var/log/valence" ]; then
|
||||
|
@ -15,3 +15,5 @@ six==1.10.0
|
||||
gunicorn==19.6.0
|
||||
python-etcd>=0.4.3 # MIT License
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
||||
oslo.config>=3.22.0 # Apache-2.0
|
||||
oslo.i18n>=2.1.0 # Apache-2.0
|
||||
|
@ -58,3 +58,7 @@ source-dir = releasenotes/source
|
||||
console_scripts =
|
||||
valence = valence.cmd.api:main
|
||||
valence-db-manager = valence.cmd.db_manager:main
|
||||
|
||||
oslo.config.opts =
|
||||
valence = valence.opts:list_opts
|
||||
valence.conf = valence.conf.opts:list_opts
|
||||
|
4
tox.ini
4
tox.ini
@ -72,3 +72,7 @@ show-source = True
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,node_modules
|
||||
import-order-style = pep8
|
||||
|
||||
[testenv:genconfig]
|
||||
commands =
|
||||
oslo-config-generator --config-file etc/os-config-generator/valence.conf --output-file etc/valence/valence.conf.sample
|
||||
|
@ -17,27 +17,38 @@ import os.path
|
||||
|
||||
import flask
|
||||
|
||||
import valence.config as cfg
|
||||
from valence.common import config
|
||||
import valence.conf
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
PROJECT_NAME = 'valence'
|
||||
_app = None
|
||||
|
||||
log_level_map = {'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL,
|
||||
'notset': logging.NOTSET}
|
||||
|
||||
|
||||
def setup_app():
|
||||
"""Return Flask application"""
|
||||
app = flask.Flask(cfg.PROJECT_NAME)
|
||||
config.parse_args(prog=PROJECT_NAME)
|
||||
app = flask.Flask(PROJECT_NAME)
|
||||
app.url_map.strict_slashes = False
|
||||
TEN_KB = 10 * 1024
|
||||
|
||||
# Configure logging
|
||||
if os.path.isfile(cfg.log_file) and os.access(cfg.log_file,
|
||||
os.W_OK):
|
||||
if os.path.isfile(CONF.api.log_file) and os.access(CONF.api.log_file,
|
||||
os.W_OK):
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
cfg.log_file, maxBytes=TEN_KB, backupCount=1)
|
||||
handler.setLevel(cfg.log_level)
|
||||
formatter = logging.Formatter(cfg.log_format)
|
||||
CONF.api.log_file, maxBytes=TEN_KB, backupCount=1)
|
||||
handler.setLevel(log_level_map.get(CONF.api.log_level))
|
||||
formatter = logging.Formatter(CONF.api.log_format)
|
||||
handler.setFormatter(formatter)
|
||||
app.logger.addHandler(handler)
|
||||
app.logger.setLevel(cfg.log_level)
|
||||
app.logger.setLevel(log_level_map.get(CONF.api.log_level))
|
||||
|
||||
@app.before_request
|
||||
def before_request_logging():
|
||||
|
@ -19,8 +19,9 @@ import gunicorn.app.base
|
||||
from gunicorn.six import iteritems
|
||||
|
||||
from valence.api.route import app as application
|
||||
from valence import config as cfg
|
||||
import valence.conf
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -44,13 +45,13 @@ class StandaloneApplication(gunicorn.app.base.BaseApplication):
|
||||
|
||||
def main():
|
||||
options = {
|
||||
'bind': '%s:%s' % (cfg.bind_host, cfg.bind_port),
|
||||
'reload': cfg.debug,
|
||||
'timeout': cfg.timeout,
|
||||
'workers': cfg.workers
|
||||
'bind': '%s:%s' % (CONF.api.bind_host, CONF.api.bind_port),
|
||||
'reload': CONF.api.debug,
|
||||
'timeout': CONF.api.timeout,
|
||||
'workers': CONF.api.workers
|
||||
}
|
||||
LOG.info(("Valence Server on http://%(host)s:%(port)s"),
|
||||
{'host': cfg.bind_host, 'port': cfg.bind_port})
|
||||
{'host': CONF.api.bind_host, 'port': CONF.api.bind_port})
|
||||
StandaloneApplication(application, options).run()
|
||||
|
||||
|
||||
|
28
valence/common/config.py
Normal file
28
valence/common/config.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from valence import version
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def parse_args(args=None, prog=None):
|
||||
CONF(
|
||||
args=args,
|
||||
project='valence',
|
||||
prog=prog,
|
||||
version=version.version_info.version_string(),
|
||||
)
|
25
valence/common/i18n.py
Normal file
25
valence/common/i18n.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
# It's based on oslo.i18n usage in OpenStack Keystone project and
|
||||
# recommendations from http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='valence')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
25
valence/conf/__init__.py
Normal file
25
valence/conf/__init__.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from valence.conf import api
|
||||
from valence.conf import etcd
|
||||
from valence.conf import podm
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
api.register_opts(CONF)
|
||||
etcd.register_opts(CONF)
|
||||
podm.register_opts(CONF)
|
81
valence/conf/api.py
Normal file
81
valence/conf/api.py
Normal file
@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from valence.common.i18n import _
|
||||
|
||||
api_service_opts = [
|
||||
cfg.PortOpt('bind_port',
|
||||
default=8181,
|
||||
help=_('The port for the valence API server.')),
|
||||
cfg.IPOpt('bind_host',
|
||||
default='127.0.0.1',
|
||||
help=_('The listen IP for the valence API server.')),
|
||||
cfg.BoolOpt('enable_ssl_api',
|
||||
default=False,
|
||||
help=_("Enable the integrated stand-alone API to service "
|
||||
"requests via HTTPS instead of HTTP. If there is a "
|
||||
"front-end service performing HTTPS offloading from "
|
||||
"the service, this option should be False; note, you "
|
||||
"will want to change public API endpoint to represent "
|
||||
"SSL termination URL with 'public_endpoint' option.")),
|
||||
cfg.IntOpt('workers',
|
||||
default=4,
|
||||
help=_('Number of workers for valence-api service. '
|
||||
'The default will be the number of CPUs available.')),
|
||||
cfg.IntOpt('timeout',
|
||||
default=1000,
|
||||
help=_('The maximum timeout to wait for valence API server '
|
||||
'to come up.')),
|
||||
cfg.IntOpt('max_limit',
|
||||
default=1000,
|
||||
help=_('The maximum number of items returned in a single '
|
||||
'response from a collection resource.')),
|
||||
cfg.StrOpt('api_paste_config',
|
||||
default='api-paste.ini',
|
||||
help=_('Configuration file for WSGI definition of API.')),
|
||||
cfg.BoolOpt('debug',
|
||||
default=False,
|
||||
help=_('Enable debug mode for valence-api service.'))
|
||||
]
|
||||
|
||||
log_option = [
|
||||
cfg.StrOpt('log_file',
|
||||
default='/var/log/valence/valence.log',
|
||||
help=_('The log file location for valence-api service')),
|
||||
cfg.StrOpt('log_level',
|
||||
default='debug',
|
||||
help=_('The granularity of Error log outputs.')),
|
||||
cfg.StrOpt('log_format',
|
||||
default='%(asctime)s %(name)-4s %(levelname)-4s %(message)s',
|
||||
help=_('The log format.')),
|
||||
]
|
||||
|
||||
api_group = cfg.OptGroup(name='api',
|
||||
title='Options for the valence-api service')
|
||||
|
||||
|
||||
ALL_OPTS = (api_service_opts + log_option)
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(api_group)
|
||||
conf.register_opts(ALL_OPTS, api_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {
|
||||
api_group: ALL_OPTS
|
||||
}
|
43
valence/conf/etcd.py
Normal file
43
valence/conf/etcd.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from valence.common.i18n import _
|
||||
|
||||
etcd_service_opts = [
|
||||
cfg.PortOpt('port',
|
||||
default=2379,
|
||||
help=_('The port for the etcd server.')),
|
||||
cfg.IPOpt('host',
|
||||
default='127.0.0.1',
|
||||
help=_('The listen IP for the etcd server.')),
|
||||
]
|
||||
|
||||
etcd_group = cfg.OptGroup(name='etcd',
|
||||
title='Options for the etcd service')
|
||||
|
||||
|
||||
ALL_OPTS = (etcd_service_opts)
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(etcd_group)
|
||||
conf.register_opts(ALL_OPTS, etcd_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {
|
||||
etcd_group: ALL_OPTS
|
||||
}
|
78
valence/conf/opts.py
Normal file
78
valence/conf/opts.py
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
"""
|
||||
This is the single point of entry to generate the sample configuration
|
||||
file for Zun. It collects all the necessary info from the other modules
|
||||
in this package. It is assumed that:
|
||||
|
||||
* every other module in this package has a 'list_opts' function which
|
||||
return a dict where
|
||||
* the keys are strings which are the group names
|
||||
* the value of each key is a list of config options for that group
|
||||
* the valence.conf package doesn't have further packages with config options
|
||||
* this module is only used in the context of sample file generation
|
||||
"""
|
||||
|
||||
import collections
|
||||
import importlib
|
||||
import os
|
||||
import pkgutil
|
||||
|
||||
LIST_OPTS_FUNC_NAME = "list_opts"
|
||||
|
||||
|
||||
def _tupleize(dct):
|
||||
"""Take the dict of options and convert to the 2-tuple format."""
|
||||
return [(key, val) for key, val in dct.items()]
|
||||
|
||||
|
||||
def list_opts():
|
||||
opts = collections.defaultdict(list)
|
||||
module_names = _list_module_names()
|
||||
imported_modules = _import_modules(module_names)
|
||||
_append_config_options(imported_modules, opts)
|
||||
return _tupleize(opts)
|
||||
|
||||
|
||||
def _list_module_names():
|
||||
module_names = []
|
||||
package_path = os.path.dirname(os.path.abspath(__file__))
|
||||
for _, modname, ispkg in pkgutil.iter_modules(path=[package_path]):
|
||||
if modname == "opts" or ispkg:
|
||||
continue
|
||||
else:
|
||||
module_names.append(modname)
|
||||
return module_names
|
||||
|
||||
|
||||
def _import_modules(module_names):
|
||||
imported_modules = []
|
||||
for modname in module_names:
|
||||
mod = importlib.import_module("valence.conf." + modname)
|
||||
if not hasattr(mod, LIST_OPTS_FUNC_NAME):
|
||||
msg = "The module 'valence.conf.%s' should have a '%s' "\
|
||||
"function which returns the config options." % \
|
||||
(modname, LIST_OPTS_FUNC_NAME)
|
||||
raise AttributeError(msg)
|
||||
else:
|
||||
imported_modules.append(mod)
|
||||
return imported_modules
|
||||
|
||||
|
||||
def _append_config_options(imported_modules, config_options):
|
||||
for mod in imported_modules:
|
||||
configs = mod.list_opts()
|
||||
for key, val in configs.items():
|
||||
config_options[key].extend(val)
|
63
valence/conf/podm.py
Normal file
63
valence/conf/podm.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from valence.common.i18n import _
|
||||
|
||||
podm_opts = [
|
||||
cfg.StrOpt('url',
|
||||
help=_('The URL of Redfish API server. (e.g.: '
|
||||
'http(s)://<IP>:<PORT>/). Required.')),
|
||||
cfg.StrOpt('username',
|
||||
help=_('User account with admin/server-profile access '
|
||||
'privilege. Although this property is not '
|
||||
'mandatory it\'s highly recommended to set a '
|
||||
'username. Optional.')),
|
||||
cfg.StrOpt('password',
|
||||
help=_('User account password. Although this property is '
|
||||
'not mandatory, it\'s highly recommended to set a '
|
||||
'password. Optional.')),
|
||||
cfg.BoolOpt('verify_ca',
|
||||
help=_('Either a boolean value, a path to a CA_BUNDLE '
|
||||
'file or directory with certificates of trusted '
|
||||
'CAs. If set to True the driver will verify the '
|
||||
'host certificates; if False the driver will '
|
||||
'ignore verifying the SSL certificate; If it\'s '
|
||||
'a path the driver will use the specified '
|
||||
'certificate or one of the certificates in the '
|
||||
'directory. Defaults to True. Optional.')),
|
||||
cfg.StrOpt('base_ext',
|
||||
default='/redfish/v1/',
|
||||
help=_('The URL extension that specifies the '
|
||||
'Redfish API version that valence will interact with')),
|
||||
]
|
||||
|
||||
|
||||
podm_group = cfg.OptGroup(name='podm',
|
||||
title='Options for the Refish API service')
|
||||
|
||||
|
||||
ALL_OPTS = (podm_opts)
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(podm_group)
|
||||
conf.register_opts(ALL_OPTS, podm_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {
|
||||
podm_group: ALL_OPTS
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
# Copyright 2016 Intel Corporation
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""This Module reads the configuration from .conf file
|
||||
and set default values if the expected values are not set
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from six.moves import configparser
|
||||
|
||||
|
||||
def get_option(section, key, default, value_type=str):
|
||||
"""Function to support default values
|
||||
|
||||
Though config fallback feature could be used
|
||||
Py 2.7 doesnt support it
|
||||
|
||||
"""
|
||||
option_value = config.get(
|
||||
section, key) if config.has_option(
|
||||
section, key) else default
|
||||
if value_type == bool:
|
||||
return False if str(option_value).lower() == 'false' else True
|
||||
else:
|
||||
return value_type(option_value)
|
||||
|
||||
PROJECT_NAME = 'valence'
|
||||
|
||||
config_file = "/etc/%s/%s.conf" % (PROJECT_NAME, PROJECT_NAME)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_file)
|
||||
|
||||
# Log settings
|
||||
log_level_map = {'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL,
|
||||
'notset': logging.NOTSET}
|
||||
|
||||
log_default_loc = "/var/log/%s/%s.log" % (PROJECT_NAME, PROJECT_NAME)
|
||||
log_default_format = "%(asctime)s %(name)-4s %(levelname)-4s %(message)s"
|
||||
log_level_name = get_option("DEFAULT", "log_level", 'error')
|
||||
|
||||
log_file = get_option("DEFAULT", "log_file", log_default_loc)
|
||||
log_level = log_level_map.get(log_level_name.lower())
|
||||
log_format = get_option("DEFAULT", "log_format", log_default_format)
|
||||
|
||||
# Server Settings
|
||||
bind_port = get_option("DEFAULT", "bind_port", 8181, int)
|
||||
bind_host = get_option("DEFAULT", "bind_host", "0.0.0.0")
|
||||
debug = get_option("DEFAULT", "debug", False, bool)
|
||||
timeout = get_option("DEFAULT", "timeout", 1000, int)
|
||||
workers = get_option("DEFAULT", "workers", 4, int)
|
||||
|
||||
# PODM Settings
|
||||
podm_url = get_option("podm", "url", "http://127.0.0.1")
|
||||
podm_user = get_option("podm", "user", "admin")
|
||||
podm_password = get_option("podm", "password", "admin")
|
||||
redfish_base_ext = get_option("podm", "redfish_base_ext", "/redfish/v1/")
|
||||
|
||||
# Database etcd Settings
|
||||
etcd_host = get_option("database_etcd", "host", "localhost")
|
||||
etcd_port = get_option("database_etcd", "port", 2379, int)
|
@ -14,17 +14,19 @@
|
||||
|
||||
import etcd
|
||||
|
||||
from valence import config
|
||||
import valence.conf
|
||||
from valence.db import models
|
||||
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
|
||||
etcd_directories = [
|
||||
models.PodManager.path,
|
||||
models.Flavor.path,
|
||||
models.ComposedNode.path
|
||||
]
|
||||
|
||||
etcd_client = etcd.Client(config.etcd_host, config.etcd_port)
|
||||
etcd_client = etcd.Client(CONF.etcd.host, CONF.etcd.port)
|
||||
|
||||
|
||||
def init_etcd_db():
|
||||
|
@ -21,15 +21,17 @@ from oslo_utils import uuidutils
|
||||
import six
|
||||
|
||||
from valence.common import singleton
|
||||
from valence import config
|
||||
import valence.conf
|
||||
from valence.db import models
|
||||
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_driver():
|
||||
return EtcdDriver(config.etcd_host, config.etcd_port)
|
||||
return EtcdDriver(CONF.etcd.host, CONF.etcd.port)
|
||||
|
||||
|
||||
def translate_to_models(etcd_resp, model_type):
|
||||
@ -53,7 +55,7 @@ def translate_to_models(etcd_resp, model_type):
|
||||
class EtcdDriver(object):
|
||||
"""etcd Driver."""
|
||||
|
||||
def __init__(self, host=config.etcd_host, port=config.etcd_port):
|
||||
def __init__(self, host=CONF.etcd.host, port=CONF.etcd.port):
|
||||
self.client = etcd.Client(host=host, port=port)
|
||||
|
||||
def create_podmanager(self, values):
|
||||
|
@ -26,23 +26,24 @@ from valence.api import link
|
||||
from valence.common import constants
|
||||
from valence.common import exception
|
||||
from valence.common import utils
|
||||
from valence import config as cfg
|
||||
import valence.conf
|
||||
from valence.redfish import tree
|
||||
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
SERVICE_ROOT = None
|
||||
|
||||
|
||||
def update_service_root():
|
||||
global SERVICE_ROOT
|
||||
resp = send_request(cfg.redfish_base_ext)
|
||||
resp = send_request(CONF.podm.base_ext)
|
||||
SERVICE_ROOT = resp.json()
|
||||
|
||||
|
||||
def get_rfs_url(serviceext):
|
||||
# Strip slash to make sure all input with/without slash
|
||||
redfish_base_ext = cfg.redfish_base_ext.strip("/")
|
||||
redfish_base_ext = CONF.podm.base_ext.strip("/")
|
||||
serviceext = serviceext.strip("/")
|
||||
|
||||
# Check whether serviceext statswith redfish_base_ext "redfish/v1", if yes,
|
||||
@ -52,7 +53,7 @@ def get_rfs_url(serviceext):
|
||||
else:
|
||||
relative_url = os.path.normpath(
|
||||
"/".join([redfish_base_ext, serviceext]))
|
||||
return requests.compat.urljoin(cfg.podm_url, relative_url)
|
||||
return requests.compat.urljoin(CONF.podm.url, relative_url)
|
||||
|
||||
|
||||
def get_base_resource_url(resource, update_services=False):
|
||||
@ -66,8 +67,8 @@ def get_base_resource_url(resource, update_services=False):
|
||||
def send_request(resource, method="GET", **kwargs):
|
||||
# The verify=false param in the request should be removed eventually
|
||||
url = get_rfs_url(resource)
|
||||
httpuser = cfg.podm_user
|
||||
httppwd = cfg.podm_password
|
||||
httpuser = CONF.podm.username
|
||||
httppwd = CONF.podm.password
|
||||
resp = None
|
||||
LOG.debug(url)
|
||||
try:
|
||||
@ -474,7 +475,7 @@ def compose_node(request_body):
|
||||
|
||||
# Allocated node successfully
|
||||
# node_url -- relative redfish url e.g redfish/v1/Nodes/1
|
||||
node_url = allocate_resp.headers['Location'].lstrip(cfg.podm_url)
|
||||
node_url = allocate_resp.headers['Location'].lstrip(CONF.podm.url)
|
||||
# node_index -- numeric index of new node e.g 1
|
||||
node_index = node_url.split('/')[-1]
|
||||
LOG.debug('Successfully allocated node:' + node_url)
|
||||
|
@ -1,9 +0,0 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from valence.api.route import app
|
||||
|
||||
|
||||
class FunctionalTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.app = app.test_client()
|
@ -0,0 +1,9 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from valence.api.route import app
|
||||
|
||||
|
||||
class FunctionalTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.app = app.test_client()
|
@ -17,16 +17,24 @@ import mock
|
||||
|
||||
from valence.api import app
|
||||
|
||||
import valence.conf
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
|
||||
|
||||
class TestApp(unittest.TestCase):
|
||||
|
||||
@mock.patch('valence.config.PROJECT_NAME')
|
||||
@mock.patch('valence.common.config.parse_args')
|
||||
@mock.patch('valence.api.app.PROJECT_NAME')
|
||||
@mock.patch('valence.api.app.flask.Flask')
|
||||
def test_setup_app_success(self, mock_Flask, mock_PROJECT_NAME):
|
||||
def test_setup_app_success(self, mock_Flask, mock_PROJECT_NAME,
|
||||
mock_parse_arg):
|
||||
CONF.set_override('log_level', 'debug', group='api')
|
||||
self.app = app.setup_app()
|
||||
mock_Flask.assert_called_with(mock_PROJECT_NAME)
|
||||
mock_parse_arg.assert_called_once_with(prog=mock_PROJECT_NAME)
|
||||
self.assertFalse(self.app.url_map.strict_slashes)
|
||||
self.app.logger.setLevel.assert_called_with(app.cfg.log_level)
|
||||
self.assertTrue(self.app.logger.setLevel.called)
|
||||
|
||||
@mock.patch('valence.api.app.setup_app')
|
||||
def test_get_app_success(self, mock_setup_app):
|
||||
|
@ -12,18 +12,23 @@
|
||||
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from valence.common import config
|
||||
config.parse_args = mock.Mock()
|
||||
|
||||
from valence.api import route
|
||||
|
||||
|
||||
class TestRoute(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.app = route.app
|
||||
self.app = route.app.test_client()
|
||||
self.api = route.api
|
||||
|
||||
def test_app(self):
|
||||
self.assertNotEqual(self.app, None)
|
||||
self.assertEqual(self.app.__class__.__name__, 'Flask')
|
||||
self.assertEqual(self.app.__class__.__name__, 'FlaskClient')
|
||||
|
||||
def test_api(self):
|
||||
self.assertNotEqual(self.api, None)
|
||||
|
0
valence/tests/unit/conf/__init__.py
Normal file
0
valence/tests/unit/conf/__init__.py
Normal file
72
valence/tests/unit/conf/test_conf.py
Normal file
72
valence/tests/unit/conf/test_conf.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2017 Intel, 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 collections
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
import six
|
||||
|
||||
from valence.conf import opts
|
||||
|
||||
|
||||
class ConfTestCase(unittest.TestCase):
|
||||
|
||||
def test_list_opts(self):
|
||||
for group, opt_list in opts.list_opts():
|
||||
if isinstance(group, six.string_types):
|
||||
self.assertEqual('DEFAULT', group)
|
||||
else:
|
||||
self.assertIsInstance(group, cfg.OptGroup)
|
||||
for opt in opt_list:
|
||||
self.assertIsInstance(opt, cfg.Opt)
|
||||
|
||||
def test_list_module_name_invalid_mods(self):
|
||||
with mock.patch('pkgutil.iter_modules') as mock_mods:
|
||||
mock_mods.return_value = [(None, 'foo', True),
|
||||
(None, 'opts', False)]
|
||||
self.assertEqual([], opts._list_module_names())
|
||||
|
||||
def test_list_module_name_valid_mods(self):
|
||||
with mock.patch('pkgutil.iter_modules') as mock_mods:
|
||||
mock_mods.return_value = [(None, 'foo', False)]
|
||||
self.assertEqual(['foo'], opts._list_module_names())
|
||||
|
||||
def test_import_mods_no_func(self):
|
||||
modules = ['foo', 'bar']
|
||||
with mock.patch('importlib.import_module') as mock_import:
|
||||
mock_import.return_value = mock.sentinel.mods
|
||||
self.assertRaises(AttributeError, opts._import_modules, modules)
|
||||
mock_import.assert_called_once_with('valence.conf.foo')
|
||||
|
||||
def test_import_mods_valid_func(self):
|
||||
modules = ['foo', 'bar']
|
||||
with mock.patch('importlib.import_module') as mock_import:
|
||||
mock_mod = mock.MagicMock()
|
||||
mock_import.return_value = mock_mod
|
||||
self.assertEqual([mock_mod, mock_mod],
|
||||
opts._import_modules(modules))
|
||||
mock_import.assert_has_calls([mock.call('valence.conf.foo'),
|
||||
mock.call('valence.conf.bar')])
|
||||
|
||||
def test_append_config(self):
|
||||
opt = collections.defaultdict(list)
|
||||
mock_module = mock.MagicMock()
|
||||
mock_conf = mock.MagicMock()
|
||||
mock_module.list_opts.return_value = mock_conf
|
||||
mock_conf.items.return_value = [('foo', 'bar')]
|
||||
opts._append_config_options([mock_module], opt)
|
||||
self.assertEqual({'foo': ['b', 'a', 'r']}, opt)
|
@ -21,16 +21,18 @@ from six.moves import http_client
|
||||
|
||||
from valence.common import constants
|
||||
from valence.common import exception
|
||||
from valence import config as cfg
|
||||
import valence.conf
|
||||
from valence.redfish import redfish
|
||||
from valence.tests.unit.fakes import redfish_fakes as fakes
|
||||
|
||||
CONF = valence.conf.CONF
|
||||
|
||||
|
||||
class TestRedfish(TestCase):
|
||||
|
||||
def test_get_rfs_url(self):
|
||||
cfg.podm_url = "https://127.0.0.1:8443"
|
||||
expected = urljoin(cfg.podm_url, "redfish/v1/Systems/1")
|
||||
CONF.podm.url = "https://127.0.0.1:8443"
|
||||
expected = urljoin(CONF.podm.url, "redfish/v1/Systems/1")
|
||||
|
||||
# test without service_ext
|
||||
result = redfish.get_rfs_url("/Systems/1/")
|
||||
@ -59,8 +61,8 @@ class TestRedfish(TestCase):
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_get_rfs_url_with_tailing_slash(self):
|
||||
cfg.podm_url = "https://127.0.0.1:8443/"
|
||||
expected = urljoin(cfg.podm_url, "redfish/v1/Systems/1")
|
||||
CONF.podm.url = "https://127.0.0.1:8443/"
|
||||
expected = urljoin(CONF.podm.url, "redfish/v1/Systems/1")
|
||||
|
||||
# test without service_ext
|
||||
result = redfish.get_rfs_url("/Systems/1/")
|
||||
@ -346,6 +348,7 @@ class TestRedfish(TestCase):
|
||||
def test_assemble_node_failed(self, mock_request, mock_get_url,
|
||||
mock_delete_node):
|
||||
"""Test allocate resource conflict when compose node"""
|
||||
CONF.set_override('url', 'http://localhost:8442/', group='podm')
|
||||
mock_get_url.return_value = '/redfish/v1/Nodes'
|
||||
|
||||
# Fake response for getting nodes root
|
||||
@ -355,7 +358,7 @@ class TestRedfish(TestCase):
|
||||
fake_node_allocation_conflict = mock.MagicMock()
|
||||
fake_node_allocation_conflict.status_code = http_client.CREATED
|
||||
fake_node_allocation_conflict.headers['Location'] = \
|
||||
os.path.normpath("/".join([cfg.podm_url, 'redfish/v1/Nodes/1']))
|
||||
os.path.normpath("/".join([CONF.podm.url, 'redfish/v1/Nodes/1']))
|
||||
|
||||
# Fake response for getting url of node assembling
|
||||
fake_node_detail = fakes.mock_request_get(fakes.fake_node_detail(),
|
||||
@ -381,6 +384,7 @@ class TestRedfish(TestCase):
|
||||
def test_assemble_node_success(self, mock_request, mock_get_url,
|
||||
mock_delete_node, mock_get_node_by_id):
|
||||
"""Test compose node successfully"""
|
||||
CONF.set_override('url', 'http://localhost:8442/', group='podm')
|
||||
mock_get_url.return_value = '/redfish/v1/Nodes'
|
||||
|
||||
# Fake response for getting nodes root
|
||||
@ -390,7 +394,7 @@ class TestRedfish(TestCase):
|
||||
fake_node_allocation_conflict = mock.MagicMock()
|
||||
fake_node_allocation_conflict.status_code = http_client.CREATED
|
||||
fake_node_allocation_conflict.headers['Location'] = \
|
||||
os.path.normpath("/".join([cfg.podm_url, 'redfish/v1/Nodes/1']))
|
||||
os.path.normpath("/".join([CONF.podm.url, 'redfish/v1/Nodes/1']))
|
||||
|
||||
# Fake response for getting url of node assembling
|
||||
fake_node_detail = fakes.mock_request_get(fakes.fake_node_detail(),
|
||||
|
18
valence/version.py
Normal file
18
valence/version.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2017 Intel, 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.
|
||||
|
||||
from pbr import version
|
||||
|
||||
version_info = version.VersionInfo('valence')
|
Loading…
Reference in New Issue
Block a user