Switch to oslo.config

This patch set adds support for generating config files
using oslo-config-generator.

Old invocation without --config-file is no longer supported.

Co-Authored-By: Dmitry Tantsur <dtantsur@redhat.com>
Change-Id: If640e6dc83c971a8f82f9b245a4496d298dfd042
Closes-Bug: #1398359
This commit is contained in:
YuikoTakada 2015-03-11 04:33:48 +00:00 committed by Dmitry Tantsur
parent b9e8e1e73f
commit 9ec4f0fbc8
22 changed files with 401 additions and 256 deletions

View File

@ -335,6 +335,16 @@ See `1.1.0 release tracking page`_ for details.
* ``overwrite_existing`` is now enabled by default. * ``overwrite_existing`` is now enabled by default.
* Running the service as
::
$ ironic-discoverd /path/to/config
is no longer supported, use
::
$ ironic-discoverd --config-file /path/to/config
**Major Features** **Major Features**
* Default to only creating a port for the NIC that the ramdisk was PXE booted * Default to only creating a port for the NIC that the ramdisk was PXE booted
@ -357,6 +367,9 @@ See `1.1.0 release tracking page`_ for details.
* The default value for ``overwrite_existing`` configuration option was * The default value for ``overwrite_existing`` configuration option was
flipped, matching the default behavior for Ironic inspection. flipped, matching the default behavior for Ironic inspection.
* Switch to `oslo.config <http://docs.openstack.org/developer/oslo.config/>`_
for configuration management (many thanks to Yuiko Takada).
**Other Changes** **Other Changes**
* New option ``add_ports`` allows precise control over which ports to add, * New option ``add_ports`` allows precise control over which ports to add,

View File

@ -1,79 +1,114 @@
[DEFAULT]
[discoverd] [discoverd]
;; Ironic and Keystone connection settings
; Authentication options are mandatory and don't have reasonable defaults.
; Keystone authentication endpoint. #
;os_auth_url = http://127.0.0.1:5000/v2.0 # From ironic_discoverd
; User name for accessing Keystone and Ironic API. #
;os_username =
; Password for accessing Keystone and Ironic API.
;os_password =
; Tenant name for accessing Keystone and Ironic API.
;os_tenant_name =
; Keystone admin endpoint.
;identity_uri = http://127.0.0.1:35357
; Number of attempts to do when trying to connect to Ironic on start up. # Keystone authentication endpoint. (string value)
;ironic_retry_attempts = 5 #os_auth_url = http://127.0.0.1:5000/v2.0
; Amount of time between attempts to connect to Ironic on start up.
;ironic_retry_period = 5
;; Firewall management settings # User name for accessing Keystone and Ironic API. (string value)
#os_username =
; Whether to manage firewall rules for PXE port. # Password for accessing Keystone and Ironic API. (string value)
;manage_firewall = true #os_password =
; Interface on which dnsmasq listens, the default is for VM's.
;dnsmasq_interface = br-ctlplane
; Amount of time in seconds, after which repeat periodic update of firewall.
;firewall_update_period = 15
;; Introspection process settings # Tenant name for accessing Keystone and Ironic API. (string value)
#os_tenant_name =
; Which MAC addresses to add as ports during introspection. Possible values: # Keystone admin endpoint. (string value)
; all (all MAC addresses), active (MAC addresses of NIC with IP addresses), #identity_uri = http://127.0.0.1:35357
; pxe (only MAC address of NIC node PXE booted from, falls back to 'active' if
; PXE MAC not supplied by the ramdisk).
;add_ports = pxe
; Timeout after which introspection is considered failed, set to 0 to disable.
;timeout = 3600
; For how much time (in seconds) to keep status information about nodes after
; introspection was finished for them. Default value is 1 week.
;node_status_keep_time = 604800
; Amount of time in seconds, after which repeat clean up of timed out nodes
; and old nodes status information.
;clean_up_period = 60
; Whether to overwrite existing values in node database.
; Disable this option to make introspection a non-destructive operation.
;overwrite_existing = true
; Whether to enable setting IPMI credentials during introspection. This is an
; experimental and not well tested feature, use at your own risk.
;enable_setting_ipmi_credentials = false
;; HTTP settings # Number of attempts to do when trying to connect to Ironic on start
# up. (integer value)
#ironic_retry_attempts = 5
; IP to listen on. # Amount of time between attempts to connect to Ironic on start up.
;listen_address = 0.0.0.0 # (integer value)
; Port to listen on. #ironic_retry_period = 5
;listen_port = 5050
; Whether to authenticate with Keystone on public HTTP endpoints.
; Note that introspection ramdisk postback endpoint is never authenticated.
;authenticate = true
;; General service settings # Whether to manage firewall rules for PXE port. (boolean value)
#manage_firewall = true
; SQLite3 database to store nodes under introspection, required. # Interface on which dnsmasq listens, the default is for VM's. (string
; Do not use :memory: here, it won't work. # value)
;database = #dnsmasq_interface = br-ctlplane
; Comma-separated list of enabled hooks for processing pipeline.
; Hook 'scheduler' updates the node with the minimum properties required by the
; Nova scheduler. Hook 'validate_interfaces' ensures that valid NIC data was
; provided by the ramdisk.
; Do not exclude these two unless you really know what you're doing.
;processing_hooks = scheduler,validate_interfaces
; Debug mode enabled/disabled.
;debug = false
;; Deprecated options # Amount of time in seconds, after which repeat periodic update of
# firewall. (integer value)
#firewall_update_period = 15
; Use add_ports # Which MAC addresses to add as ports during introspection. Possible
;ports_for_inactive_interfaces = false # values: all (all MAC addresses), active (MAC addresses of NIC with
# IP addresses), pxe (only MAC address of NIC node PXE booted from,
# falls back to "active" if PXE MAC is not supplied by the ramdisk).
# (string value)
# Allowed values: all, active, pxe
#add_ports = pxe
# Timeout after which introspection is considered failed, set to 0 to
# disable. (integer value)
#timeout = 3600
# For how much time (in seconds) to keep status information about
# nodes after introspection was finished for them. Default value is 1
# week. (integer value)
#node_status_keep_time = 604800
# Amount of time in seconds, after which repeat clean up of timed out
# nodes and old nodes status information. (integer value)
#clean_up_period = 60
# Whether to overwrite existing values in node database. Disable this
# option to make introspection a non-destructive operation. (boolean
# value)
#overwrite_existing = true
# Whether to enable setting IPMI credentials during introspection.
# This is an experimental and not well tested feature, use at your own
# risk. (boolean value)
#enable_setting_ipmi_credentials = false
# IP to listen on. (string value)
#listen_address = 0.0.0.0
# Port to listen on. (integer value)
#listen_port = 5050
# Whether to authenticate with Keystone on public HTTP endpoints. Note
# that introspection ramdisk postback endpoint is never authenticated.
# (boolean value)
#authenticate = true
# SQLite3 database to store nodes under introspection, required. Do
# not use :memory: here, it won't work. (string value)
#database =
# Comma-separated list of enabled hooks for processing pipeline. Hook
# 'scheduler' updates the node with the minimum properties required by
# the Nova scheduler. Hook 'validate_interfaces' ensures that valid
# NIC data was provided by the ramdisk.Do not exclude these two unless
# you really know what you're doing. (string value)
#processing_hooks = scheduler,validate_interfaces
# Debug mode enabled/disabled. (boolean value)
#debug = false
# DEPRECATED: use add_ports. (boolean value)
#ports_for_inactive_interfaces = false
[edeploy]
#
# From ironic_discoverd.plugins.edeploy
#
# (string value)
#lockname = /var/lock/discoverd.lock
# (string value)
#configdir = /etc/edeploy

View File

@ -29,7 +29,6 @@ import mock
import requests import requests
from ironic_discoverd import client from ironic_discoverd import client
from ironic_discoverd import conf
from ironic_discoverd import main from ironic_discoverd import main
from ironic_discoverd.test import base from ironic_discoverd.test import base
from ironic_discoverd import utils from ironic_discoverd import utils
@ -41,8 +40,9 @@ os_auth_url = http://url
os_username = user os_username = user
os_password = password os_password = password
os_tenant_name = tenant os_tenant_name = tenant
manage_firewall = false manage_firewall = False
enable_setting_ipmi_credentials = true enable_setting_ipmi_credentials = True
database = %(db_file)s
""" """
ROOT = './functest/env' ROOT = './functest/env'
@ -57,8 +57,6 @@ JQ = "https://stedolan.github.io/jq/download/linux64/jq"
class Test(base.NodeTest): class Test(base.NodeTest):
def setUp(self): def setUp(self):
super(Test, self).setUp() super(Test, self).setUp()
conf.CONF.set('discoverd', 'manage_firewall', 'false')
conf.CONF.set('discoverd', 'enable_setting_ipmi_credentials', 'true')
self.node.properties.clear() self.node.properties.clear()
self.cli = utils.get_client() self.cli = utils.get_client()
@ -172,12 +170,12 @@ def run(client_mock, keystone_mock):
d = tempfile.mkdtemp() d = tempfile.mkdtemp()
try: try:
conf_file = os.path.join(d, 'test.conf') conf_file = os.path.join(d, 'test.conf')
db_file = os.path.join(d, 'test.db')
with open(conf_file, 'wb') as fp: with open(conf_file, 'wb') as fp:
fp.write(CONF) fp.write(CONF % {'db_file': db_file})
sys.argv[1:] = ['--config-file', conf_file]
base.init_test_conf()
eventlet.greenthread.spawn_n(main.main) eventlet.greenthread.spawn_n(main.main,
args=['--config-file', conf_file])
eventlet.greenthread.sleep(1) eventlet.greenthread.sleep(1)
suite = unittest.TestLoader().loadTestsFromTestCase(Test) suite = unittest.TestLoader().loadTestsFromTestCase(Test)
res = unittest.TextTestRunner().run(suite) res = unittest.TextTestRunner().run(suite)

View File

@ -11,57 +11,115 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import functools from oslo_config import cfg
from six.moves import configparser
# TODO(dtantsur): switch to oslo.db VALID_ADD_PORTS_VALUES = ('all', 'active', 'pxe')
DEFAULTS = {
# Keystone credentials SERVICE_OPTS = [
'os_auth_url': 'http://127.0.0.1:5000/v2.0', cfg.StrOpt('os_auth_url',
'identity_uri': 'http://127.0.0.1:35357', default='http://127.0.0.1:5000/v2.0',
# Ironic and Keystone connection settings help='Keystone authentication endpoint.'),
'ironic_retry_attempts': '5', cfg.StrOpt('os_username',
'ironic_retry_period': '5', default='',
# Firewall management settings help='User name for accessing Keystone and Ironic API.'),
'manage_firewall': 'true', cfg.StrOpt('os_password',
'dnsmasq_interface': 'br-ctlplane', default='',
'firewall_update_period': '15', help='Password for accessing Keystone and Ironic API.',
# Introspection process settings secret=True),
'add_ports': 'pxe', cfg.StrOpt('os_tenant_name',
'timeout': '3600', default='',
'node_status_keep_time': '604800', help='Tenant name for accessing Keystone and Ironic API.'),
'clean_up_period': '60', cfg.StrOpt('identity_uri',
'overwrite_existing': 'true', default='http://127.0.0.1:35357',
'enable_setting_ipmi_credentials': 'false', help='Keystone admin endpoint.'),
# HTTP settings cfg.IntOpt('ironic_retry_attempts',
'listen_address': '0.0.0.0', default=5,
'listen_port': '5050', help='Number of attempts to do when trying to connect to '
'authenticate': 'true', 'Ironic on start up.'),
# General service settings cfg.IntOpt('ironic_retry_period',
'processing_hooks': 'scheduler,validate_interfaces', default=5,
'debug': 'false', help='Amount of time between attempts to connect to Ironic '
} 'on start up.'),
cfg.BoolOpt('manage_firewall',
default=True,
help='Whether to manage firewall rules for PXE port.'),
cfg.StrOpt('dnsmasq_interface',
default='br-ctlplane',
help='Interface on which dnsmasq listens, the default is for '
'VM\'s.'),
cfg.IntOpt('firewall_update_period',
default=15,
help='Amount of time in seconds, after which repeat periodic '
'update of firewall.'),
cfg.StrOpt('add_ports',
default='pxe',
help='Which MAC addresses to add as ports during '
'introspection. Possible values: '
'all (all MAC addresses), active (MAC addresses of NIC with IP '
'addresses), pxe (only MAC address of NIC node PXE booted '
'from, falls back to "active" if PXE MAC is not supplied '
'by the ramdisk).',
choices=VALID_ADD_PORTS_VALUES),
cfg.IntOpt('timeout',
default=3600,
help='Timeout after which introspection is considered failed, '
'set to 0 to disable.'),
cfg.IntOpt('node_status_keep_time',
default=604800,
help='For how much time (in seconds) to keep status '
'information about nodes after introspection was '
'finished for them. Default value is 1 week.'),
cfg.IntOpt('clean_up_period',
default=60,
help='Amount of time in seconds, after which repeat clean up '
'of timed out nodes and old nodes status information.'),
cfg.BoolOpt('overwrite_existing',
default=True,
help='Whether to overwrite existing values in node database. '
'Disable this option to make introspection a '
'non-destructive operation.'),
cfg.BoolOpt('enable_setting_ipmi_credentials',
default=False,
help='Whether to enable setting IPMI credentials during '
'introspection. This is an experimental and not well '
'tested feature, use at your own risk.'),
cfg.StrOpt('listen_address',
default='0.0.0.0',
help='IP to listen on.'),
cfg.IntOpt('listen_port',
default=5050,
help='Port to listen on.'),
cfg.BoolOpt('authenticate',
default=True,
help='Whether to authenticate with Keystone on public HTTP '
'endpoints. Note that introspection ramdisk postback '
'endpoint is never authenticated.'),
cfg.StrOpt('database',
default='',
help='SQLite3 database to store nodes under introspection, '
'required. Do not use :memory: here, it won\'t work.'),
cfg.StrOpt('processing_hooks',
default='scheduler,validate_interfaces',
help='Comma-separated list of enabled hooks for processing '
'pipeline. Hook \'scheduler\' updates the node with the '
'minimum properties required by the Nova scheduler. '
'Hook \'validate_interfaces\' ensures that valid NIC '
'data was provided by the ramdisk.'
'Do not exclude these two unless you really know what '
'you\'re doing.'),
cfg.BoolOpt('debug',
default=False,
help='Debug mode enabled/disabled.'),
cfg.BoolOpt('ports_for_inactive_interfaces',
default=False,
help='DEPRECATED: use add_ports.'),
]
cfg.CONF.register_opts(SERVICE_OPTS, group='discoverd')
def with_default(func): def list_opts():
@functools.wraps(func) return [
def wrapper(section, option, default=None): ('discoverd', SERVICE_OPTS)
try: ]
return func(section, option)
except (configparser.NoSectionError, configparser.NoOptionError):
return default
return wrapper
def init_conf():
global CONF, get, getint, getboolean, read
CONF = configparser.ConfigParser(defaults=DEFAULTS)
get = with_default(CONF.get)
getint = with_default(CONF.getint)
getboolean = with_default(CONF.getboolean)
read = CONF.read
init_conf()

View File

@ -15,9 +15,9 @@ import logging
import subprocess import subprocess
from eventlet import semaphore from eventlet import semaphore
from oslo_config import cfg
from ironic_discoverd.common.i18n import _LE from ironic_discoverd.common.i18n import _LE
from ironic_discoverd import conf
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd import utils from ironic_discoverd import utils
@ -27,6 +27,7 @@ NEW_CHAIN = 'discovery_temp'
CHAIN = 'discovery' CHAIN = 'discovery'
INTERFACE = None INTERFACE = None
LOCK = semaphore.BoundedSemaphore() LOCK = semaphore.BoundedSemaphore()
CONF = cfg.CONF
def _iptables(*args, **kwargs): def _iptables(*args, **kwargs):
@ -50,11 +51,11 @@ def init():
Must be called one on start-up. Must be called one on start-up.
""" """
if not conf.getboolean('discoverd', 'manage_firewall'): if not CONF.discoverd.manage_firewall:
return return
global INTERFACE global INTERFACE
INTERFACE = conf.get('discoverd', 'dnsmasq_interface') INTERFACE = CONF.discoverd.dnsmasq_interface
_clean_up(CHAIN) _clean_up(CHAIN)
# Not really needed, but helps to validate that we have access to iptables # Not really needed, but helps to validate that we have access to iptables
_iptables('-N', CHAIN) _iptables('-N', CHAIN)
@ -86,7 +87,7 @@ def update_filters(ironic=None):
:param ironic: Ironic client instance, optional. :param ironic: Ironic client instance, optional.
""" """
if not conf.getboolean('discoverd', 'manage_firewall'): if not CONF.discoverd.manage_firewall:
return return
assert INTERFACE is not None assert INTERFACE is not None

View File

@ -18,13 +18,15 @@ import string
import eventlet import eventlet
from ironicclient import exceptions from ironicclient import exceptions
from oslo_config import cfg
from ironic_discoverd.common.i18n import _, _LI, _LW from ironic_discoverd.common.i18n import _, _LI, _LW
from ironic_discoverd import conf
from ironic_discoverd import firewall from ironic_discoverd import firewall
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
LOG = logging.getLogger("ironic_discoverd.introspect") LOG = logging.getLogger("ironic_discoverd.introspect")
# See http://specs.openstack.org/openstack/ironic-specs/specs/kilo/new-ironic-state-machine.html # noqa # See http://specs.openstack.org/openstack/ironic-specs/specs/kilo/new-ironic-state-machine.html # noqa
@ -34,7 +36,7 @@ PASSWORD_MAX_LENGTH = 20 # IPMI v2.0
def _validate_ipmi_credentials(node, new_ipmi_credentials): def _validate_ipmi_credentials(node, new_ipmi_credentials):
if not conf.getboolean('discoverd', 'enable_setting_ipmi_credentials'): if not CONF.discoverd.enable_setting_ipmi_credentials:
raise utils.Error( raise utils.Error(
_('IPMI credentials setup is disabled in configuration')) _('IPMI credentials setup is disabled in configuration'))
@ -156,4 +158,4 @@ def _background_introspect(ironic, cached_node):
LOG.info(_LI('Introspection environment is ready for node %(node)s, ' LOG.info(_LI('Introspection environment is ready for node %(node)s, '
'manual power on is required within %(timeout)d seconds') % 'manual power on is required within %(timeout)d seconds') %
{'node': cached_node.uuid, {'node': cached_node.uuid,
'timeout': conf.getint('discoverd', 'timeout')}) 'timeout': CONF.discoverd.timeout})

View File

@ -14,23 +14,26 @@
import eventlet import eventlet
eventlet.monkey_patch() eventlet.monkey_patch()
import argparse
import functools import functools
import json import json
import logging import logging
from oslo_utils import uuidutils
import sys import sys
import flask import flask
from oslo_config import cfg
from oslo_utils import uuidutils
from ironic_discoverd.common.i18n import _, _LE, _LW from ironic_discoverd.common.i18n import _, _LE, _LW
from ironic_discoverd import conf # Import configuration options
from ironic_discoverd import conf # noqa
from ironic_discoverd import firewall from ironic_discoverd import firewall
from ironic_discoverd import introspect from ironic_discoverd import introspect
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd import process from ironic_discoverd import process
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
app = flask.Flask(__name__) app = flask.Flask(__name__)
LOG = logging.getLogger('ironic_discoverd.main') LOG = logging.getLogger('ironic_discoverd.main')
@ -132,9 +135,9 @@ def check_ironic_available():
2. Keystone has already started 2. Keystone has already started
3. Ironic has already started 3. Ironic has already started
""" """
attempts = conf.getint('discoverd', 'ironic_retry_attempts') attempts = CONF.discoverd.ironic_retry_attempts
assert attempts >= 0 assert attempts >= 0
retry_period = conf.getint('discoverd', 'ironic_retry_period') retry_period = CONF.discoverd.ironic_retry_period
LOG.debug('Trying to connect to Ironic') LOG.debug('Trying to connect to Ironic')
for i in range(attempts + 1): # one attempt always required for i in range(attempts + 1): # one attempt always required
try: try:
@ -150,14 +153,8 @@ def check_ironic_available():
eventlet.greenthread.sleep(retry_period) eventlet.greenthread.sleep(retry_period)
def config_shim(args):
"""Make new argument parsing method backwards compatible."""
if len(args) == 2 and args[1][0] != '-':
return ['--config-file', args[1]]
def init(): def init():
if conf.getboolean('discoverd', 'authenticate'): if CONF.discoverd.authenticate:
utils.add_auth_middleware(app) utils.add_auth_middleware(app)
else: else:
LOG.warning(_LW('Starting unauthenticated, please check' LOG.warning(_LW('Starting unauthenticated, please check'
@ -166,29 +163,21 @@ def init():
node_cache.init() node_cache.init()
check_ironic_available() check_ironic_available()
if conf.getboolean('discoverd', 'manage_firewall'): if CONF.discoverd.manage_firewall:
firewall.init() firewall.init()
period = conf.getint('discoverd', 'firewall_update_period') period = CONF.discoverd.firewall_update_period
eventlet.greenthread.spawn_n(periodic_update, period) eventlet.greenthread.spawn_n(periodic_update, period)
if conf.getint('discoverd', 'timeout') > 0: if CONF.discoverd.timeout > 0:
period = conf.getint('discoverd', 'clean_up_period') period = CONF.discoverd.clean_up_period
eventlet.greenthread.spawn_n(periodic_clean_up, period) eventlet.greenthread.spawn_n(periodic_clean_up, period)
else: else:
LOG.warning(_LW('Timeout is disabled in configuration')) LOG.warning(_LW('Timeout is disabled in configuration'))
def main(): # pragma: no cover def main(args=sys.argv[1:]): # pragma: no cover
old_args = config_shim(sys.argv) CONF(args, project='ironic-discoverd')
parser = argparse.ArgumentParser(description='''Hardware introspection debug = CONF.discoverd.debug
service for OpenStack Ironic.
''')
parser.add_argument('--config-file', dest='config', required=True)
# if parse_args is passed None it uses sys.argv instead.
args = parser.parse_args(old_args)
conf.read(args.config)
debug = conf.getboolean('discoverd', 'debug')
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)
for third_party in ('urllib3.connectionpool', for third_party in ('urllib3.connectionpool',
@ -198,12 +187,7 @@ def main(): # pragma: no cover
logging.getLogger('ironicclient.common.http').setLevel( logging.getLogger('ironicclient.common.http').setLevel(
logging.INFO if debug else logging.ERROR) logging.INFO if debug else logging.ERROR)
if old_args:
LOG.warning(_LW('"ironic-discoverd <config-file>" syntax is deprecated'
' use "ironic-discoverd --config-file <config-file>"'
' instead'))
init() init()
app.run(debug=debug, app.run(debug=debug,
host=conf.get('discoverd', 'listen_address'), host=CONF.discoverd.listen_address,
port=conf.getint('discoverd', 'listen_port')) port=CONF.discoverd.listen_port)

View File

@ -21,10 +21,13 @@ import sqlite3
import sys import sys
import time import time
from oslo_config import cfg
from ironic_discoverd.common.i18n import _, _LC, _LE from ironic_discoverd.common.i18n import _, _LC, _LE
from ironic_discoverd import conf
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
LOG = logging.getLogger("ironic_discoverd.node_cache") LOG = logging.getLogger("ironic_discoverd.node_cache")
_DB_NAME = None _DB_NAME = None
@ -128,7 +131,7 @@ def init():
"""Initialize the database.""" """Initialize the database."""
global _DB_NAME global _DB_NAME
_DB_NAME = conf.get('discoverd', 'database', default='').strip() _DB_NAME = CONF.discoverd.database.strip()
if not _DB_NAME: if not _DB_NAME:
LOG.critical(_LC('Configuration option discoverd.database' LOG.critical(_LC('Configuration option discoverd.database'
' should be set')) ' should be set'))
@ -262,13 +265,13 @@ def clean_up():
:return: list of timed out node UUID's :return: list of timed out node UUID's
""" """
status_keep_threshold = (time.time() - status_keep_threshold = (time.time() -
conf.getint('discoverd', 'node_status_keep_time')) CONF.discoverd.node_status_keep_time)
with _db() as db: with _db() as db:
db.execute('delete from nodes where finished_at < ?', db.execute('delete from nodes where finished_at < ?',
(status_keep_threshold,)) (status_keep_threshold,))
timeout = conf.getint('discoverd', 'timeout') timeout = CONF.discoverd.timeout
if timeout <= 0: if timeout <= 0:
return [] return []

View File

@ -15,10 +15,12 @@
import abc import abc
from oslo_config import cfg
import six import six
from stevedore import named from stevedore import named
from ironic_discoverd import conf
CONF = cfg.CONF
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
@ -72,7 +74,7 @@ def processing_hooks_manager(*args):
global _HOOKS_MGR global _HOOKS_MGR
if _HOOKS_MGR is None: if _HOOKS_MGR is None:
names = [x.strip() names = [x.strip()
for x in conf.get('discoverd', 'processing_hooks').split(',') for x in CONF.discoverd.processing_hooks.split(',')
if x.strip()] if x.strip()]
_HOOKS_MGR = named.NamedExtensionManager('ironic_discoverd.hooks', _HOOKS_MGR = named.NamedExtensionManager('ironic_discoverd.hooks',
names=names, names=names,

View File

@ -21,16 +21,33 @@ import logging
from hardware import matcher from hardware import matcher
from hardware import state from hardware import state
from oslo_config import cfg
from ironic_discoverd.common.i18n import _, _LW from ironic_discoverd.common.i18n import _, _LW
from ironic_discoverd import conf
from ironic_discoverd.plugins import base from ironic_discoverd.plugins import base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
LOG = logging.getLogger('ironic_discoverd.plugins.edeploy') LOG = logging.getLogger('ironic_discoverd.plugins.edeploy')
EDEPLOY_OPTS = [
cfg.StrOpt('lockname',
default='/var/lock/discoverd.lock'),
cfg.StrOpt('configdir',
default='/etc/edeploy'),
]
CONF.register_opts(EDEPLOY_OPTS, group='edeploy')
def list_opts():
return [
('edeploy', EDEPLOY_OPTS)
]
class eDeployHook(base.ProcessingHook): class eDeployHook(base.ProcessingHook):
"""Interact with eDeploy ramdisk for discovery data processing hooks.""" """Interact with eDeploy ramdisk for discovery data processing hooks."""
@ -60,9 +77,8 @@ class eDeployHook(base.ProcessingHook):
sobj = None sobj = None
try: try:
sobj = state.State(lockname=conf.get('edeploy', 'lockname', sobj = state.State(CONF.edeploy.lockname)
'/var/lock/discoverd.lock')) sobj.load(CONF.edeploy.configdir)
sobj.load(conf.get('edeploy', 'configdir', '/etc/edeploy'))
prof, var = sobj.find_match(hw_items) prof, var = sobj.find_match(hw_items)
var['profile'] = prof var['profile'] = prof

View File

@ -16,11 +16,15 @@
import logging import logging
import sys import sys
from oslo_config import cfg
from ironic_discoverd.common.i18n import _, _LC, _LI, _LW from ironic_discoverd.common.i18n import _, _LC, _LI, _LW
from ironic_discoverd import conf from ironic_discoverd import conf
from ironic_discoverd.plugins import base from ironic_discoverd.plugins import base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
LOG = logging.getLogger('ironic_discoverd.plugins.standard') LOG = logging.getLogger('ironic_discoverd.plugins.standard')
@ -44,7 +48,7 @@ class SchedulerHook(base.ProcessingHook):
def before_update(self, node, ports, node_info): def before_update(self, node, ports, node_info):
"""Update node with scheduler properties.""" """Update node with scheduler properties."""
overwrite = conf.getboolean('discoverd', 'overwrite_existing') overwrite = CONF.discoverd.overwrite_existing
patch = [{'op': 'add', 'path': '/properties/%s' % key, patch = [{'op': 'add', 'path': '/properties/%s' % key,
'value': str(node_info[key])} 'value': str(node_info[key])}
for key in self.KEYS for key in self.KEYS
@ -52,27 +56,24 @@ class SchedulerHook(base.ProcessingHook):
return patch, {} return patch, {}
VALID_ADD_PORTS_VALUES = ('all', 'active', 'pxe')
class ValidateInterfacesHook(base.ProcessingHook): class ValidateInterfacesHook(base.ProcessingHook):
"""Hook to validate network interfaces.""" """Hook to validate network interfaces."""
def __init__(self): def __init__(self):
if conf.get('discoverd', 'add_ports') not in VALID_ADD_PORTS_VALUES: if CONF.discoverd.add_ports not in conf.VALID_ADD_PORTS_VALUES:
LOG.critical(_LC('Accepted values for [discoverd]add_ports are ' LOG.critical(_LC('Accepted values for [discoverd]add_ports are '
'%(valid)s, got %(actual)s'), '%(valid)s, got %(actual)s'),
{'valid': VALID_ADD_PORTS_VALUES, {'valid': conf.VALID_ADD_PORTS_VALUES,
'actual': conf.get('discoverd', 'add_ports')}) 'actual': CONF.discoverd.add_ports})
sys.exit(1) sys.exit(1)
def _ports_to_add(self): def _ports_to_add(self):
if conf.getboolean('discoverd', 'ports_for_inactive_interfaces'): if CONF.discoverd.ports_for_inactive_interfaces:
LOG.warning(_LW('Using deprecated option ' LOG.warning(_LW('Using deprecated option '
'[discoverd]ports_for_inactive_interfaces')) '[discoverd]ports_for_inactive_interfaces'))
return 'all' return 'all'
else: else:
return conf.get('discoverd', 'add_ports') return CONF.discoverd.add_ports
def before_processing(self, node_info): def before_processing(self, node_info):
"""Validate information about network interfaces.""" """Validate information about network interfaces."""

View File

@ -15,18 +15,31 @@ import tempfile
import unittest import unittest
import mock import mock
from oslo_config import cfg
from ironic_discoverd.common import i18n from ironic_discoverd.common import i18n
from ironic_discoverd import conf # Import configuration options
from ironic_discoverd import conf # noqa
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd.plugins import base as plugins_base from ironic_discoverd.plugins import base as plugins_base
CONF = cfg.CONF
def init_test_conf(): def init_test_conf():
db_file = tempfile.NamedTemporaryFile() try:
conf.init_conf() # Functional tests
conf.CONF.add_section('discoverd') CONF.reload_config_files()
conf.CONF.set('discoverd', 'database', db_file.name) # Unit tests
except Exception:
CONF.reset()
CONF.register_group(cfg.OptGroup('discoverd'))
if not CONF.discoverd.database:
# Might be set in functional tests
db_file = tempfile.NamedTemporaryFile()
CONF.set_override('database', db_file.name, 'discoverd')
else:
db_file = None
node_cache._DB_NAME = None node_cache._DB_NAME = None
return db_file return db_file
@ -36,7 +49,8 @@ class BaseTest(unittest.TestCase):
super(BaseTest, self).setUp() super(BaseTest, self).setUp()
self.db_file = init_test_conf() self.db_file = init_test_conf()
self.db = node_cache._db() self.db = node_cache._db()
self.addCleanup(lambda: self.db_file.close()) if self.db_file:
self.addCleanup(lambda: self.db_file.close())
plugins_base._HOOKS_MGR = None plugins_base._HOOKS_MGR = None
for name in ('_', '_LI', '_LW', '_LE', '_LC'): for name in ('_', '_LI', '_LW', '_LE', '_LC'):
patch = mock.patch.object(i18n, name, lambda s: s) patch = mock.patch.object(i18n, name, lambda s: s)

View File

@ -14,14 +14,16 @@
import eventlet import eventlet
from ironicclient import exceptions from ironicclient import exceptions
import mock import mock
from oslo_config import cfg
from ironic_discoverd import conf
from ironic_discoverd import firewall from ironic_discoverd import firewall
from ironic_discoverd import introspect from ironic_discoverd import introspect
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd.test import base as test_base from ironic_discoverd.test import base as test_base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
class BaseTest(test_base.NodeTest): class BaseTest(test_base.NodeTest):
def setUp(self): def setUp(self):
@ -266,7 +268,7 @@ class TestIntrospect(BaseTest):
class TestSetIpmiCredentials(BaseTest): class TestSetIpmiCredentials(BaseTest):
def setUp(self): def setUp(self):
super(TestSetIpmiCredentials, self).setUp() super(TestSetIpmiCredentials, self).setUp()
conf.CONF.set('discoverd', 'enable_setting_ipmi_credentials', 'true') CONF.set_override('enable_setting_ipmi_credentials', True, 'discoverd')
self.new_creds = ('user', 'password') self.new_creds = ('user', 'password')
self.cached_node.options['new_ipmi_credentials'] = self.new_creds self.cached_node.options['new_ipmi_credentials'] = self.new_creds
self.node.maintenance = True self.node.maintenance = True
@ -288,7 +290,8 @@ class TestSetIpmiCredentials(BaseTest):
'new_ipmi_credentials', self.new_creds) 'new_ipmi_credentials', self.new_creds)
def test_disabled(self, client_mock, add_mock, filters_mock): def test_disabled(self, client_mock, add_mock, filters_mock):
conf.CONF.set('discoverd', 'enable_setting_ipmi_credentials', 'false') CONF.set_override('enable_setting_ipmi_credentials', False,
'discoverd')
self._prepare(client_mock) self._prepare(client_mock)
self.assertRaisesRegexp(utils.Error, 'disabled', self.assertRaisesRegexp(utils.Error, 'disabled',

View File

@ -18,7 +18,6 @@ import eventlet
import mock import mock
from oslo_utils import uuidutils from oslo_utils import uuidutils
from ironic_discoverd import conf
from ironic_discoverd import introspect from ironic_discoverd import introspect
from ironic_discoverd import main from ironic_discoverd import main
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
@ -27,6 +26,9 @@ from ironic_discoverd.plugins import example as example_plugin
from ironic_discoverd import process from ironic_discoverd import process
from ironic_discoverd.test import base as test_base from ironic_discoverd.test import base as test_base
from ironic_discoverd import utils from ironic_discoverd import utils
from oslo_config import cfg
CONF = cfg.CONF
class TestApi(test_base.BaseTest): class TestApi(test_base.BaseTest):
@ -34,12 +36,12 @@ class TestApi(test_base.BaseTest):
super(TestApi, self).setUp() super(TestApi, self).setUp()
main.app.config['TESTING'] = True main.app.config['TESTING'] = True
self.app = main.app.test_client() self.app = main.app.test_client()
conf.CONF.set('discoverd', 'authenticate', 'false') CONF.set_override('authenticate', False, 'discoverd')
self.uuid = uuidutils.generate_uuid() self.uuid = uuidutils.generate_uuid()
@mock.patch.object(introspect, 'introspect', autospec=True) @mock.patch.object(introspect, 'introspect', autospec=True)
def test_introspect_no_authentication(self, introspect_mock): def test_introspect_no_authentication(self, introspect_mock):
conf.CONF.set('discoverd', 'authenticate', 'false') CONF.set_override('authenticate', False, 'discoverd')
res = self.app.post('/v1/introspection/%s' % self.uuid) res = self.app.post('/v1/introspection/%s' % self.uuid)
self.assertEqual(202, res.status_code) self.assertEqual(202, res.status_code)
introspect_mock.assert_called_once_with(self.uuid, introspect_mock.assert_called_once_with(self.uuid,
@ -47,7 +49,7 @@ class TestApi(test_base.BaseTest):
@mock.patch.object(introspect, 'introspect', autospec=True) @mock.patch.object(introspect, 'introspect', autospec=True)
def test_introspect_set_ipmi_credentials(self, introspect_mock): def test_introspect_set_ipmi_credentials(self, introspect_mock):
conf.CONF.set('discoverd', 'authenticate', 'false') CONF.set_override('authenticate', False, 'discoverd')
res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&' res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&'
'new_ipmi_password=password' % self.uuid) 'new_ipmi_password=password' % self.uuid)
self.assertEqual(202, res.status_code) self.assertEqual(202, res.status_code)
@ -57,7 +59,7 @@ class TestApi(test_base.BaseTest):
@mock.patch.object(introspect, 'introspect', autospec=True) @mock.patch.object(introspect, 'introspect', autospec=True)
def test_introspect_set_ipmi_credentials_no_user(self, introspect_mock): def test_introspect_set_ipmi_credentials_no_user(self, introspect_mock):
conf.CONF.set('discoverd', 'authenticate', 'false') CONF.set_override('authenticate', False, 'discoverd')
res = self.app.post('/v1/introspection/%s?' res = self.app.post('/v1/introspection/%s?'
'new_ipmi_password=password' % self.uuid) 'new_ipmi_password=password' % self.uuid)
self.assertEqual(202, res.status_code) self.assertEqual(202, res.status_code)
@ -79,7 +81,7 @@ class TestApi(test_base.BaseTest):
@mock.patch.object(introspect, 'introspect', autospec=True) @mock.patch.object(introspect, 'introspect', autospec=True)
def test_introspect_failed_authentication(self, introspect_mock, def test_introspect_failed_authentication(self, introspect_mock,
auth_mock): auth_mock):
conf.CONF.set('discoverd', 'authenticate', 'true') CONF.set_override('authenticate', True, 'discoverd')
auth_mock.side_effect = utils.Error('Boom', code=403) auth_mock.side_effect = utils.Error('Boom', code=403)
res = self.app.post('/v1/introspection/%s' % self.uuid, res = self.app.post('/v1/introspection/%s' % self.uuid,
headers={'X-Auth-Token': 'token'}) headers={'X-Auth-Token': 'token'})
@ -106,7 +108,8 @@ class TestApi(test_base.BaseTest):
@mock.patch.object(process, 'process', autospec=True) @mock.patch.object(process, 'process', autospec=True)
def test_continue(self, process_mock): def test_continue(self, process_mock):
conf.CONF.set('discoverd', 'authenticate', 'true') # should be ignored # should be ignored
CONF.set_override('authenticate', True, 'discoverd')
process_mock.return_value = [42] process_mock.return_value = [42]
res = self.app.post('/v1/continue', data='"JSON"') res = self.app.post('/v1/continue', data='"JSON"')
self.assertEqual(200, res.status_code) self.assertEqual(200, res.status_code)
@ -157,10 +160,10 @@ class TestCheckIronicAvailable(test_base.BaseTest):
self.assertEqual(2, client_mock.call_count) self.assertEqual(2, client_mock.call_count)
cli.driver.list.assert_called_once_with() cli.driver.list.assert_called_once_with()
sleep_mock.assert_called_once_with( sleep_mock.assert_called_once_with(
conf.getint('discoverd', 'ironic_retry_period')) CONF.discoverd.ironic_retry_period)
def test_failed(self, client_mock, sleep_mock): def test_failed(self, client_mock, sleep_mock):
attempts = conf.getint('discoverd', 'ironic_retry_attempts') attempts = CONF.discoverd.ironic_retry_attempts
client_mock.side_effect = RuntimeError() client_mock.side_effect = RuntimeError()
self.assertRaises(RuntimeError, main.check_ironic_available) self.assertRaises(RuntimeError, main.check_ironic_available)
self.assertEqual(1 + attempts, client_mock.call_count) self.assertEqual(1 + attempts, client_mock.call_count)
@ -174,7 +177,7 @@ class TestPlugins(unittest.TestCase):
'before_update', autospec=True) 'before_update', autospec=True)
def test_hook(self, mock_post, mock_pre): def test_hook(self, mock_post, mock_pre):
plugins_base._HOOKS_MGR = None plugins_base._HOOKS_MGR = None
conf.CONF.set('discoverd', 'processing_hooks', 'example') CONF.set_override('processing_hooks', 'example', 'discoverd')
mgr = plugins_base.processing_hooks_manager() mgr = plugins_base.processing_hooks_manager()
mgr.map_method('before_processing', 'node_info') mgr.map_method('before_processing', 'node_info')
mock_pre.assert_called_once_with(mock.ANY, 'node_info') mock_pre.assert_called_once_with(mock.ANY, 'node_info')
@ -185,15 +188,3 @@ class TestPlugins(unittest.TestCase):
def test_manager_is_cached(self): def test_manager_is_cached(self):
self.assertIs(plugins_base.processing_hooks_manager(), self.assertIs(plugins_base.processing_hooks_manager(),
plugins_base.processing_hooks_manager()) plugins_base.processing_hooks_manager())
class TestConfigShim(unittest.TestCase):
def test_old_style_invocation(self):
self.assertEqual(main.config_shim(
['ironic-discoverd', '/etc/conf']),
['--config-file', '/etc/conf'])
def test_new_style_returns_None(self):
self.assertEqual(main.config_shim(
['ironic-discoverd', '--config-file', '/etc/conf']),
None)

View File

@ -17,12 +17,14 @@ import time
import unittest import unittest
import mock import mock
from oslo_config import cfg
from ironic_discoverd import conf
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd.test import base as test_base from ironic_discoverd.test import base as test_base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
class TestNodeCache(test_base.NodeTest): class TestNodeCache(test_base.NodeTest):
def test_add_node(self): def test_add_node(self):
@ -156,7 +158,7 @@ class TestNodeCacheCleanUp(test_base.NodeTest):
'values(?, ?, ?)', (self.uuid, 'foo', 'bar')) 'values(?, ?, ?)', (self.uuid, 'foo', 'bar'))
def test_no_timeout(self): def test_no_timeout(self):
conf.CONF.set('discoverd', 'timeout', '0') CONF.set_override('timeout', 0, 'discoverd')
self.assertFalse(node_cache.clean_up()) self.assertFalse(node_cache.clean_up())
@ -190,7 +192,7 @@ class TestNodeCacheCleanUp(test_base.NodeTest):
'values(?, ?, ?)', (self.uuid + '1', 'values(?, ?, ?)', (self.uuid + '1',
self.started_at, self.started_at,
self.started_at + 60)) self.started_at + 60))
conf.CONF.set('discoverd', 'timeout', '99') CONF.set_override('timeout', 99, 'discoverd')
time_mock.return_value = self.started_at + 100 time_mock.return_value = self.started_at + 100
self.assertEqual([self.uuid], node_cache.clean_up()) self.assertEqual([self.uuid], node_cache.clean_up())
@ -206,7 +208,7 @@ class TestNodeCacheCleanUp(test_base.NodeTest):
'select * from options').fetchall()) 'select * from options').fetchall())
def test_old_status(self): def test_old_status(self):
conf.CONF.set('discoverd', 'node_status_keep_time', '42') CONF.set_override('node_status_keep_time', 42, 'discoverd')
with self.db: with self.db:
self.db.execute('update nodes set finished_at=?', self.db.execute('update nodes set finished_at=?',
(time.time() - 100,)) (time.time() - 100,))
@ -270,13 +272,11 @@ class TestNodeInfoFinished(test_base.NodeTest):
class TestInit(unittest.TestCase): class TestInit(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestInit, self).setUp() super(TestInit, self).setUp()
conf.init_conf()
conf.CONF.add_section('discoverd')
node_cache._DB_NAME = None node_cache._DB_NAME = None
def test_ok(self): def test_ok(self):
with tempfile.NamedTemporaryFile() as db_file: with tempfile.NamedTemporaryFile() as db_file:
conf.CONF.set('discoverd', 'database', db_file.name) CONF.set_override('database', db_file.name, 'discoverd')
node_cache.init() node_cache.init()
self.assertIsNotNone(node_cache._DB_NAME) self.assertIsNotNone(node_cache._DB_NAME)
@ -285,11 +285,12 @@ class TestInit(unittest.TestCase):
def test_create_dir(self): def test_create_dir(self):
temp = tempfile.mkdtemp() temp = tempfile.mkdtemp()
conf.CONF.set('discoverd', 'database', CONF.set_override('database', os.path.join(temp, 'dir', 'file'),
os.path.join(temp, 'dir', 'file')) 'discoverd')
node_cache.init() node_cache.init()
def test_no_database(self): def test_no_database(self):
CONF.set_override('database', '', 'discoverd')
self.assertRaises(SystemExit, node_cache.init) self.assertRaises(SystemExit, node_cache.init)

View File

@ -16,12 +16,14 @@ import os
from hardware import cmdb from hardware import cmdb
from hardware import state from hardware import state
import mock import mock
from oslo_config import cfg
from ironic_discoverd import conf
from ironic_discoverd.plugins import edeploy from ironic_discoverd.plugins import edeploy
from ironic_discoverd.test import base as test_base from ironic_discoverd.test import base as test_base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
def fake_load(obj, cfg_dir): def fake_load(obj, cfg_dir):
obj._cfg_dir = cfg_dir obj._cfg_dir = cfg_dir
@ -36,11 +38,10 @@ class TestEdeploy(test_base.NodeTest):
def setUp(self): def setUp(self):
super(TestEdeploy, self).setUp() super(TestEdeploy, self).setUp()
conf.init_conf()
conf.CONF.add_section('edeploy')
basedir = os.path.dirname(os.path.abspath(__file__)) basedir = os.path.dirname(os.path.abspath(__file__))
conf.CONF.set('edeploy', 'configdir', os.path.join(basedir, CONF.set_override('configdir',
'edeploy_conf')) os.path.join(basedir, 'edeploy_conf'),
'edeploy')
def test_hook(self): def test_hook(self):
hook = edeploy.eDeployHook() hook = edeploy.eDeployHook()

View File

@ -17,8 +17,8 @@ import time
import eventlet import eventlet
from ironicclient import exceptions from ironicclient import exceptions
import mock import mock
from oslo_config import cfg
from ironic_discoverd import conf
from ironic_discoverd import firewall from ironic_discoverd import firewall
from ironic_discoverd import node_cache from ironic_discoverd import node_cache
from ironic_discoverd.plugins import example as example_plugin from ironic_discoverd.plugins import example as example_plugin
@ -27,12 +27,15 @@ from ironic_discoverd import process
from ironic_discoverd.test import base as test_base from ironic_discoverd.test import base as test_base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
class BaseTest(test_base.NodeTest): class BaseTest(test_base.NodeTest):
def setUp(self): def setUp(self):
super(BaseTest, self).setUp() super(BaseTest, self).setUp()
conf.CONF.set('discoverd', 'processing_hooks', CONF.set_override('processing_hooks',
'ramdisk_error,scheduler,validate_interfaces') 'ramdisk_error,scheduler,validate_interfaces',
'discoverd')
self.started_at = time.time() self.started_at = time.time()
self.all_macs = self.macs + ['DE:AD:BE:EF:DE:AD'] self.all_macs = self.macs + ['DE:AD:BE:EF:DE:AD']
self.pxe_mac = self.macs[1] self.pxe_mac = self.macs[1]
@ -139,7 +142,7 @@ class TestProcess(BaseTest):
@prepare_mocks @prepare_mocks
def test_add_ports_active(self, cli, pop_mock, process_mock): def test_add_ports_active(self, cli, pop_mock, process_mock):
conf.CONF.set('discoverd', 'add_ports', 'active') CONF.set_override('add_ports', 'active', 'discoverd')
res = process.process(self.data) res = process.process(self.data)
@ -159,7 +162,7 @@ class TestProcess(BaseTest):
@prepare_mocks @prepare_mocks
def test_add_ports_all(self, cli, pop_mock, process_mock): def test_add_ports_all(self, cli, pop_mock, process_mock):
conf.CONF.set('discoverd', 'add_ports', 'all') CONF.set_override('add_ports', 'all', 'discoverd')
res = process.process(self.data) res = process.process(self.data)
@ -196,8 +199,7 @@ class TestProcess(BaseTest):
@prepare_mocks @prepare_mocks
def test_ports_for_inactive(self, cli, pop_mock, process_mock): def test_ports_for_inactive(self, cli, pop_mock, process_mock):
conf.CONF.set('discoverd', 'ports_for_inactive_interfaces', 'true') CONF.set_override('ports_for_inactive_interfaces', True, 'discoverd')
conf.CONF.remove_option('discoverd', 'add_ports')
del self.data['boot_interface'] del self.data['boot_interface']
process.process(self.data) process.process(self.data)
@ -317,8 +319,10 @@ class TestProcess(BaseTest):
class TestProcessNode(BaseTest): class TestProcessNode(BaseTest):
def setUp(self): def setUp(self):
super(TestProcessNode, self).setUp() super(TestProcessNode, self).setUp()
conf.CONF.set('discoverd', 'processing_hooks', CONF.set_override('processing_hooks',
'ramdisk_error,scheduler,validate_interfaces,example') 'ramdisk_error,scheduler,validate_interfaces,'
'example',
'discoverd')
self.validate_attempts = 5 self.validate_attempts = 5
self.data['macs'] = self.macs # validate_interfaces hook self.data['macs'] = self.macs # validate_interfaces hook
self.ports = self.all_ports self.ports = self.all_ports
@ -373,7 +377,7 @@ class TestProcessNode(BaseTest):
finished_mock.assert_called_once_with(mock.ANY) finished_mock.assert_called_once_with(mock.ANY)
def test_overwrite_disabled(self, filters_mock, post_hook_mock): def test_overwrite_disabled(self, filters_mock, post_hook_mock):
conf.CONF.set('discoverd', 'overwrite_existing', 'false') CONF.set_override('overwrite_existing', False, 'discoverd')
patch = [ patch = [
{'op': 'add', 'path': '/properties/cpus', 'value': '2'}, {'op': 'add', 'path': '/properties/cpus', 'value': '2'},
{'op': 'add', 'path': '/properties/memory_mb', 'value': '1024'}, {'op': 'add', 'path': '/properties/memory_mb', 'value': '1024'},
@ -508,5 +512,5 @@ class TestProcessNode(BaseTest):
class TestValidateInterfacesHook(test_base.BaseTest): class TestValidateInterfacesHook(test_base.BaseTest):
def test_wrong_add_ports(self): def test_wrong_add_ports(self):
conf.CONF.set('discoverd', 'add_ports', 'foobar') CONF.set_override('add_ports', 'foobar', 'discoverd')
self.assertRaises(SystemExit, std_plugins.ValidateInterfacesHook) self.assertRaises(SystemExit, std_plugins.ValidateInterfacesHook)

View File

@ -17,22 +17,24 @@ import eventlet
from ironicclient import exceptions from ironicclient import exceptions
from keystonemiddleware import auth_token from keystonemiddleware import auth_token
import mock import mock
from oslo_config import cfg
from ironic_discoverd import conf
from ironic_discoverd.test import base from ironic_discoverd.test import base
from ironic_discoverd import utils from ironic_discoverd import utils
CONF = cfg.CONF
class TestCheckAuth(base.BaseTest): class TestCheckAuth(base.BaseTest):
def setUp(self): def setUp(self):
super(TestCheckAuth, self).setUp() super(TestCheckAuth, self).setUp()
conf.CONF.set('discoverd', 'authenticate', 'true') CONF.set_override('authenticate', True, 'discoverd')
@mock.patch.object(auth_token, 'AuthProtocol') @mock.patch.object(auth_token, 'AuthProtocol')
def test_middleware(self, mock_auth): def test_middleware(self, mock_auth):
conf.CONF.set('discoverd', 'os_username', 'admin') CONF.set_override('os_username', 'admin', 'discoverd')
conf.CONF.set('discoverd', 'os_tenant_name', 'admin') CONF.set_override('os_tenant_name', 'admin', 'discoverd')
conf.CONF.set('discoverd', 'os_password', 'password') CONF.set_override('os_password', 'password', 'discoverd')
app = mock.Mock(wsgi_app=mock.sentinel.app) app = mock.Mock(wsgi_app=mock.sentinel.app)
utils.add_auth_middleware(app) utils.add_auth_middleware(app)
@ -60,7 +62,7 @@ class TestCheckAuth(base.BaseTest):
self.assertRaises(utils.Error, utils.check_auth, request) self.assertRaises(utils.Error, utils.check_auth, request)
def test_disabled(self): def test_disabled(self):
conf.CONF.set('discoverd', 'authenticate', 'false') CONF.set_override('authenticate', False, 'discoverd')
request = mock.Mock(headers={'X-Identity-Status': 'Invalid'}) request = mock.Mock(headers={'X-Identity-Status': 'Invalid'})
utils.check_auth(request) utils.check_auth(request)

View File

@ -19,16 +19,15 @@ import eventlet
from ironicclient import client from ironicclient import client
from ironicclient import exceptions from ironicclient import exceptions
from keystonemiddleware import auth_token from keystonemiddleware import auth_token
from oslo_config import cfg
import six import six
from ironic_discoverd.common.i18n import _, _LE, _LW from ironic_discoverd.common.i18n import _, _LE, _LW
from ironic_discoverd import conf
CONF = cfg.CONF
LOG = logging.getLogger('ironic_discoverd.utils') LOG = logging.getLogger('ironic_discoverd.utils')
OS_ARGS = ('os_password', 'os_username', 'os_auth_url', 'os_tenant_name')
MIDDLEWARE_ARGS = ('admin_password', 'admin_user', 'auth_uri',
'admin_tenant_name')
RETRY_COUNT = 12 RETRY_COUNT = 12
RETRY_DELAY = 5 RETRY_DELAY = 5
@ -44,7 +43,10 @@ class Error(Exception):
def get_client(): # pragma: no cover def get_client(): # pragma: no cover
"""Get Ironic client instance.""" """Get Ironic client instance."""
args = dict((k, conf.get('discoverd', k)) for k in OS_ARGS) args = dict({'os_password': CONF.discoverd.os_password,
'os_username': CONF.discoverd.os_username,
'os_auth_url': CONF.discoverd.os_auth_url,
'os_tenant_name': CONF.discoverd.os_tenant_name})
return client.get_client(1, **args) return client.get_client(1, **args)
@ -53,10 +55,12 @@ def add_auth_middleware(app):
:param app: application. :param app: application.
""" """
auth_conf = {key: conf.get('discoverd', value) auth_conf = dict({'admin_password': CONF.discoverd.os_password,
for (key, value) in zip(MIDDLEWARE_ARGS, OS_ARGS)} 'admin_user': CONF.discoverd.os_username,
'auth_uri': CONF.discoverd.os_auth_url,
'admin_tenant_name': CONF.discoverd.os_tenant_name})
auth_conf['delay_auth_decision'] = True auth_conf['delay_auth_decision'] = True
auth_conf['identity_uri'] = conf.get('discoverd', 'identity_uri') auth_conf['identity_uri'] = CONF.discoverd.identity_uri
app.wsgi_app = auth_token.AuthProtocol(app.wsgi_app, auth_conf) app.wsgi_app = auth_token.AuthProtocol(app.wsgi_app, auth_conf)
@ -66,7 +70,7 @@ def check_auth(request):
:param request: Flask request :param request: Flask request
:raises: utils.Error if access is denied :raises: utils.Error if access is denied
""" """
if not conf.getboolean('discoverd', 'authenticate'): if not CONF.discoverd.authenticate:
return return
if request.headers.get('X-Identity-Status').lower() == 'invalid': if request.headers.get('X-Identity-Status').lower() == 'invalid':
raise Error(_('Authentication required'), code=401) raise Error(_('Authentication required'), code=401)

View File

@ -9,6 +9,7 @@ python-ironicclient>=0.2.1
python-keystoneclient>=1.1.0 python-keystoneclient>=1.1.0
python-openstackclient>=1.0.0 python-openstackclient>=1.0.0
requests>=2.2.0,!=2.4.0 requests>=2.2.0,!=2.4.0
oslo.config>=1.9.0 # Apache-2.0
oslo.i18n>=1.3.0 # Apache-2.0 oslo.i18n>=1.3.0 # Apache-2.0
oslo.utils>=1.2.0 # Apache-2.0 oslo.utils>=1.2.0 # Apache-2.0
six>=1.7.0 six>=1.7.0

View File

@ -46,6 +46,10 @@ setup(
"baremetal_introspection_start = ironic_discoverd.shell:StartCommand", "baremetal_introspection_start = ironic_discoverd.shell:StartCommand",
"baremetal_introspection_status = ironic_discoverd.shell:StatusCommand", "baremetal_introspection_status = ironic_discoverd.shell:StatusCommand",
], ],
'oslo.config.opts': [
"ironic_discoverd = ironic_discoverd.conf:list_opts",
"ironic_discoverd.plugins.edeploy = ironic_discoverd.plugins.edeploy:list_opts",
],
}, },
classifiers = [ classifiers = [
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',

View File

@ -36,3 +36,10 @@ deps =
-r{toxinidir}/plugin-requirements.txt -r{toxinidir}/plugin-requirements.txt
commands = commands =
python functest/run.py python functest/run.py
[testenv:genconfig]
commands =
oslo-config-generator \
--output-file example.conf \
--namespace ironic_discoverd \
--namespace ironic_discoverd.plugins.edeploy