diff --git a/config-generator.conf b/config-generator.conf
index b3c8f42b2..fae4268a6 100644
--- a/config-generator.conf
+++ b/config-generator.conf
@@ -1,11 +1,6 @@
 [DEFAULT]
 output_file = example.conf
 namespace = ironic_inspector
-namespace = ironic_inspector.common.ironic
-namespace = ironic_inspector.common.swift
-namespace = ironic_inspector.plugins.capabilities
-namespace = ironic_inspector.plugins.discovery
-namespace = ironic_inspector.plugins.pci_devices
 namespace = keystonemiddleware.auth_token
 namespace = oslo.db
 namespace = oslo.log
diff --git a/example.conf b/example.conf
index 92bfef706..0b341f22f 100644
--- a/example.conf
+++ b/example.conf
@@ -183,7 +183,7 @@
 [capabilities]
 
 #
-# From ironic_inspector.plugins.capabilities
+# From ironic_inspector
 #
 
 # Whether to store the boot mode (BIOS or UEFI). (boolean value)
@@ -340,7 +340,7 @@
 [discovery]
 
 #
-# From ironic_inspector.plugins.discovery
+# From ironic_inspector
 #
 
 # The name of the Ironic driver used by the enroll hook when creating
@@ -408,7 +408,7 @@
 [ironic]
 
 #
-# From ironic_inspector.common.ironic
+# From ironic_inspector
 #
 
 # Authentication URL (string value)
@@ -763,7 +763,7 @@
 [pci_devices]
 
 #
-# From ironic_inspector.plugins.pci_devices
+# From ironic_inspector
 #
 
 # An alias for PCI device identified by 'vendor_id' and 'product_id'
@@ -868,7 +868,7 @@
 [swift]
 
 #
-# From ironic_inspector.common.swift
+# From ironic_inspector
 #
 
 # Authentication URL (string value)
diff --git a/ironic_inspector/common/ironic.py b/ironic_inspector/common/ironic.py
index f61cf6e4f..7541453fe 100644
--- a/ironic_inspector/common/ironic.py
+++ b/ironic_inspector/common/ironic.py
@@ -31,41 +31,6 @@ VALID_STATES = {'enroll', 'manageable', 'inspecting', 'inspect failed'}
 # 1.19 is API version, which supports port.pxe_enabled
 DEFAULT_IRONIC_API_VERSION = '1.19'
 
-IRONIC_GROUP = 'ironic'
-
-IRONIC_OPTS = [
-    cfg.StrOpt('os_region',
-               help=_('Keystone region used to get Ironic endpoints.')),
-    cfg.StrOpt('auth_strategy',
-               default='keystone',
-               choices=('keystone', 'noauth'),
-               help=_('Method to use for authentication: noauth or '
-                      'keystone.')),
-    cfg.StrOpt('ironic_url',
-               default='http://localhost:6385/',
-               help=_('Ironic API URL, used to set Ironic API URL when '
-                      'auth_strategy option is noauth to work with standalone '
-                      'Ironic without keystone.')),
-    cfg.StrOpt('os_service_type',
-               default='baremetal',
-               help=_('Ironic service type.')),
-    cfg.StrOpt('os_endpoint_type',
-               default='internalURL',
-               help=_('Ironic endpoint type.')),
-    cfg.IntOpt('retry_interval',
-               default=2,
-               help=_('Interval between retries in case of conflict error '
-                      '(HTTP 409).')),
-    cfg.IntOpt('max_retries',
-               default=30,
-               help=_('Maximum number of retries in case of conflict error '
-                      '(HTTP 409).')),
-]
-
-
-CONF.register_opts(IRONIC_OPTS, group=IRONIC_GROUP)
-keystone.register_auth_opts(IRONIC_GROUP)
-
 IRONIC_SESSION = None
 
 
@@ -124,7 +89,7 @@ def get_client(token=None,
     else:
         global IRONIC_SESSION
         if not IRONIC_SESSION:
-            IRONIC_SESSION = keystone.get_session(IRONIC_GROUP)
+            IRONIC_SESSION = keystone.get_session('ironic')
         if token is None:
             args = {'session': IRONIC_SESSION,
                     'region_name': CONF.ironic.os_region}
@@ -182,7 +147,3 @@ def get_node(node_id, ironic=None, **kwargs):
     except ironic_exc.HttpError as exc:
         raise utils.Error(_("Cannot get node %(node)s: %(exc)s") %
                           {'node': node_id, 'exc': exc})
-
-
-def list_opts():
-    return keystone.add_auth_options(IRONIC_OPTS, IRONIC_GROUP)
diff --git a/ironic_inspector/common/keystone.py b/ironic_inspector/common/keystone.py
index 28bf6290f..52dc6afc9 100644
--- a/ironic_inspector/common/keystone.py
+++ b/ironic_inspector/common/keystone.py
@@ -33,7 +33,7 @@ def get_session(group):
     return session
 
 
-def add_auth_options(options, group):
+def add_auth_options(options):
 
     def add_options(opts, opts_to_add):
         for new_opt in opts_to_add:
@@ -53,4 +53,4 @@ def add_auth_options(options, group):
         add_options(opts, loading.get_auth_plugin_conf_options(plugin))
     add_options(opts, loading.get_session_conf_options())
     opts.sort(key=lambda x: x.name)
-    return [(group, opts)]
+    return opts
diff --git a/ironic_inspector/common/service_utils.py b/ironic_inspector/common/service_utils.py
index b690c1a3b..ba09428b9 100644
--- a/ironic_inspector/common/service_utils.py
+++ b/ironic_inspector/common/service_utils.py
@@ -10,19 +10,20 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+from oslo_config import cfg
 from oslo_log import log
 
-from ironic_inspector import conf
+from ironic_inspector.conf import opts
 
 LOG = log.getLogger(__name__)
-CONF = conf.cfg.CONF
+CONF = cfg.CONF
 
 
 def prepare_service(args=None):
     args = [] if args is None else args
     log.register_options(CONF)
-    conf.set_config_defaults()
-    conf.parse_args(args)
+    opts.set_config_defaults()
+    opts.parse_args(args)
     log.setup(CONF, 'ironic_inspector')
 
     LOG.debug("Configuration:")
diff --git a/ironic_inspector/common/swift.py b/ironic_inspector/common/swift.py
index 547a2d22d..5bf2c8d1b 100644
--- a/ironic_inspector/common/swift.py
+++ b/ironic_inspector/common/swift.py
@@ -26,34 +26,6 @@ from ironic_inspector import utils
 CONF = cfg.CONF
 
 
-SWIFT_GROUP = 'swift'
-SWIFT_OPTS = [
-    cfg.IntOpt('max_retries',
-               default=2,
-               help=_('Maximum number of times to retry a Swift request, '
-                      'before failing.')),
-    cfg.IntOpt('delete_after',
-               default=0,
-               help=_('Number of seconds that the Swift object will last '
-                      'before being deleted. (set to 0 to never delete the '
-                      'object).')),
-    cfg.StrOpt('container',
-               default='ironic-inspector',
-               help=_('Default Swift container to use when creating '
-                      'objects.')),
-    cfg.StrOpt('os_service_type',
-               default='object-store',
-               help=_('Swift service type.')),
-    cfg.StrOpt('os_endpoint_type',
-               default='internalURL',
-               help=_('Swift endpoint type.')),
-    cfg.StrOpt('os_region',
-               help=_('Keystone region to get endpoint for.')),
-]
-
-CONF.register_opts(SWIFT_OPTS, group=SWIFT_GROUP)
-keystone.register_auth_opts(SWIFT_GROUP)
-
 OBJECT_NAME_PREFIX = 'inspector_data'
 SWIFT_SESSION = None
 
@@ -77,7 +49,7 @@ class SwiftAPI(object):
         """
         global SWIFT_SESSION
         if not SWIFT_SESSION:
-            SWIFT_SESSION = keystone.get_session(SWIFT_GROUP)
+            SWIFT_SESSION = keystone.get_session('swift')
 
         self.connection = swift_client.Connection(session=SWIFT_SESSION)
 
@@ -166,7 +138,3 @@ def get_introspection_data(uuid, suffix=None):
     if suffix is not None:
         swift_object_name = '%s-%s' % (swift_object_name, suffix)
     return swift_api.get_object(swift_object_name)
-
-
-def list_opts():
-    return keystone.add_auth_options(SWIFT_OPTS, SWIFT_GROUP)
diff --git a/ironic_inspector/conf.py b/ironic_inspector/conf.py
deleted file mode 100644
index b8c724578..000000000
--- a/ironic_inspector/conf.py
+++ /dev/null
@@ -1,283 +0,0 @@
-# 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 oslo_log import log
-from oslo_middleware import cors
-
-from ironic_inspector.common.i18n import _
-from ironic_inspector import version
-
-
-MIN_VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Minimum-Version'
-MAX_VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Maximum-Version'
-VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Version'
-
-VALID_ADD_PORTS_VALUES = ('all', 'active', 'pxe', 'disabled')
-VALID_KEEP_PORTS_VALUES = ('all', 'present', 'added')
-VALID_STORE_DATA_VALUES = ('none', 'swift')
-
-
-IPTABLES_OPTS = [
-    cfg.BoolOpt('manage_firewall',
-                default=True,
-                # NOTE(milan) this filter driver will be replaced by
-                # a dnsmasq filter driver
-                deprecated_for_removal=True,
-                deprecated_group='firewall',
-                help=_('Whether to manage firewall rules for PXE port. '
-                       'This configuration option was deprecated in favor of '
-                       'the ``driver`` option in the ``pxe_filter`` section. '
-                       'Please, use the ``noop`` filter driver to disable the '
-                       'firewall filtering or the ``iptables`` filter driver '
-                       'to enable it.')),
-    cfg.StrOpt('dnsmasq_interface',
-               default='br-ctlplane',
-               deprecated_group='firewall',
-               help=_('Interface on which dnsmasq listens, the default is for '
-                      'VM\'s.')),
-    cfg.StrOpt('firewall_chain',
-               default='ironic-inspector',
-               deprecated_group='firewall',
-               help=_('iptables chain name to use.')),
-    cfg.ListOpt('ethoib_interfaces',
-                deprecated_group='firewall',
-                default=[],
-                help=_('List of Etherent Over InfiniBand interfaces '
-                       'on the Inspector host which are used for physical '
-                       'access to the DHCP network. Multiple interfaces would '
-                       'be attached to a bond or bridge specified in '
-                       'dnsmasq_interface. The MACs of the InfiniBand nodes '
-                       'which are not in desired state are going to be '
-                       'blacklisted based on the list of neighbor MACs '
-                       'on these interfaces.')),
-]
-
-PROCESSING_OPTS = [
-    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.StrOpt('keep_ports',
-               default='all',
-               help=_('Which ports (already present on a node) to keep after '
-                      'introspection. Possible values: all (do not delete '
-                      'anything), present (keep ports which MACs were present '
-                      'in introspection data), added (keep only MACs that we '
-                      'added during introspection).'),
-               choices=VALID_KEEP_PORTS_VALUES),
-    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.StrOpt('default_processing_hooks',
-               default='ramdisk_error,root_disk_selection,scheduler,'
-                       'validate_interfaces,capabilities,pci_devices',
-               help=_('Comma-separated list of default 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.StrOpt('processing_hooks',
-               default='$default_processing_hooks',
-               help=_('Comma-separated list of enabled hooks for processing '
-                      'pipeline. The default for this is '
-                      '$default_processing_hooks, hooks can be added before '
-                      'or after the defaults like this: '
-                      '"prehook,$default_processing_hooks,posthook".')),
-    cfg.StrOpt('ramdisk_logs_dir',
-               help=_('If set, logs from ramdisk will be stored in this '
-                      'directory.')),
-    cfg.BoolOpt('always_store_ramdisk_logs',
-                default=False,
-                help=_('Whether to store ramdisk logs even if it did not '
-                       'return an error message (dependent upon '
-                       '"ramdisk_logs_dir" option being set).')),
-    cfg.StrOpt('node_not_found_hook',
-               help=_('The name of the hook to run when inspector receives '
-                      'inspection information from a node it isn\'t already '
-                      'aware of. This hook is ignored by default.')),
-    cfg.StrOpt('store_data',
-               default='none',
-               choices=VALID_STORE_DATA_VALUES,
-               help=_('Method for storing introspection data. If set to \'none'
-                      '\', introspection data will not be stored.')),
-    cfg.StrOpt('store_data_location',
-               help=_('Name of the key to store the location of stored data '
-                      'in the extra column of the Ironic database.')),
-    cfg.BoolOpt('disk_partitioning_spacing',
-                default=True,
-                help=_('Whether to leave 1 GiB of disk size untouched for '
-                       'partitioning. Only has effect when used with the IPA '
-                       'as a ramdisk, for older ramdisk local_gb is '
-                       'calculated on the ramdisk side.')),
-    cfg.StrOpt('ramdisk_logs_filename_format',
-               default='{uuid}_{dt:%Y%m%d-%H%M%S.%f}.tar.gz',
-               help=_('File name template for storing ramdisk logs. The '
-                      'following replacements can be used: '
-                      '{uuid} - node UUID or "unknown", '
-                      '{bmc} - node BMC address or "unknown", '
-                      '{dt} - current UTC date and time, '
-                      '{mac} - PXE booting MAC or "unknown".')),
-    cfg.BoolOpt('power_off',
-                default=True,
-                help=_('Whether to power off a node after introspection.')),
-]
-
-SERVICE_OPTS = [
-    cfg.StrOpt('listen_address',
-               default='0.0.0.0',
-               help=_('IP to listen on.')),
-    cfg.PortOpt('listen_port',
-                default=5050,
-                help=_('Port to listen on.')),
-    cfg.StrOpt('auth_strategy',
-               default='keystone',
-               choices=('keystone', 'noauth'),
-               help=_('Authentication method used on the ironic-inspector '
-                      'API. Either "noauth" or "keystone" are currently valid '
-                      'options. "noauth" will disable all authentication.')),
-    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=0,
-               help=_('For how much time (in seconds) to keep status '
-                      'information about nodes after introspection was '
-                      'finished for them. Set to 0 (the default) '
-                      'to disable the timeout.'),
-               deprecated_for_removal=True),
-    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('use_ssl',
-                default=False,
-                help=_('SSL Enabled/Disabled')),
-    cfg.StrOpt('ssl_cert_path',
-               default='',
-               help=_('Path to SSL certificate')),
-    cfg.StrOpt('ssl_key_path',
-               default='',
-               help=_('Path to SSL key')),
-    cfg.IntOpt('max_concurrency',
-               default=1000, min=2,
-               help=_('The green thread pool size.')),
-    cfg.IntOpt('introspection_delay',
-               default=5,
-               help=_('Delay (in seconds) between two introspections.')),
-    cfg.ListOpt('ipmi_address_fields',
-                default=['ilo_address', 'drac_host', 'drac_address',
-                         'cimc_address'],
-                help=_('Ironic driver_info fields that are equivalent '
-                       'to ipmi_address.')),
-    cfg.StrOpt('rootwrap_config',
-               default="/etc/ironic-inspector/rootwrap.conf",
-               help=_('Path to the rootwrap configuration file to use for '
-                      'running commands as root')),
-    cfg.IntOpt('api_max_limit', default=1000, min=1,
-               help=_('Limit the number of elements an API list-call returns'))
-]
-
-
-PXE_FILTER_OPTS = [
-    cfg.StrOpt('driver', default='iptables',
-               help=_('PXE boot filter driver to use, such as iptables')),
-    cfg.IntOpt('sync_period', default=15, min=0,
-               deprecated_name='firewall_update_period',
-               deprecated_group='firewall',
-               help=_('Amount of time in seconds, after which repeat periodic '
-                      'update of the filter.')),
-]
-
-DNSMASQ_PXE_FILTER_OPTS = [
-    cfg.StrOpt('dhcp_hostsdir',
-               default='/var/lib/ironic-inspector/dhcp-hostsdir',
-               help=_('The MAC address cache directory, exposed to dnsmasq.'
-                      'This directory is expected to be in exclusive control '
-                      'of the driver.')),
-    cfg.BoolOpt('purge_dhcp_hostsdir', default=True,
-                help=_('Purge the hostsdir upon driver initialization. '
-                       'Setting to false should only be performed when the '
-                       'deployment of inspector is such that there are '
-                       'multiple processes executing inside of the same host '
-                       'and namespace. In this case, the Operator is '
-                       'responsible for setting up a custom cleaning '
-                       'facility.')),
-    cfg.StrOpt('dnsmasq_start_command', default='',
-               help=_('A (shell) command line to start the dnsmasq service '
-                      'upon filter initialization. Default: don\'t start.')),
-    cfg.StrOpt('dnsmasq_stop_command', default='',
-               help=_('A (shell) command line to stop the dnsmasq service '
-                      'upon inspector (error) exit. Default: don\'t stop.')),
-]
-
-
-cfg.CONF.register_opts(SERVICE_OPTS)
-cfg.CONF.register_opts(IPTABLES_OPTS, group='iptables')
-cfg.CONF.register_opts(PROCESSING_OPTS, group='processing')
-cfg.CONF.register_opts(PXE_FILTER_OPTS, 'pxe_filter')
-cfg.CONF.register_opts(DNSMASQ_PXE_FILTER_OPTS, group='dnsmasq_pxe_filter')
-
-
-def list_opts():
-    return [
-        ('', SERVICE_OPTS),
-        ('iptables', IPTABLES_OPTS),
-        ('processing', PROCESSING_OPTS),
-        ('pxe_filter', PXE_FILTER_OPTS),
-        ('dnsmasq_pxe_filter', DNSMASQ_PXE_FILTER_OPTS),
-    ]
-
-
-def set_config_defaults():
-    """This method updates all configuration default values."""
-    log.set_defaults(default_log_levels=['sqlalchemy=WARNING',
-                                         'iso8601=WARNING',
-                                         'requests=WARNING',
-                                         'urllib3.connectionpool=WARNING',
-                                         'keystonemiddleware=WARNING',
-                                         'swiftclient=WARNING',
-                                         'keystoneauth=WARNING',
-                                         'ironicclient=WARNING'])
-    set_cors_middleware_defaults()
-
-
-def set_cors_middleware_defaults():
-    """Update default configuration options for oslo.middleware."""
-    # TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/
-    cfg.set_defaults(
-        cors.CORS_OPTS,
-        allow_headers=['X-Auth-Token',
-                       MIN_VERSION_HEADER,
-                       MAX_VERSION_HEADER,
-                       VERSION_HEADER],
-        allow_methods=['GET', 'POST', 'PUT', 'HEAD',
-                       'PATCH', 'DELETE', 'OPTIONS']
-    )
-
-
-def parse_args(args, default_config_files=None):
-    cfg.CONF(args,
-             project='ironic-inspector',
-             version=version.version_info.release_string(),
-             default_config_files=default_config_files)
diff --git a/ironic_inspector/conf/__init__.py b/ironic_inspector/conf/__init__.py
new file mode 100644
index 000000000..e1720cd35
--- /dev/null
+++ b/ironic_inspector/conf/__init__.py
@@ -0,0 +1,39 @@
+#    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 oslo_config import cfg
+
+from ironic_inspector.conf import capabilities
+from ironic_inspector.conf import default
+from ironic_inspector.conf import discovery
+from ironic_inspector.conf import dnsmasq_pxe_filter
+from ironic_inspector.conf import iptables
+from ironic_inspector.conf import ironic
+from ironic_inspector.conf import pci_devices
+from ironic_inspector.conf import processing
+from ironic_inspector.conf import pxe_filter
+from ironic_inspector.conf import swift
+
+
+CONF = cfg.CONF
+
+
+capabilities.register_opts(CONF)
+discovery.register_opts(CONF)
+default.register_opts(CONF)
+dnsmasq_pxe_filter.register_opts(CONF)
+iptables.register_opts(CONF)
+ironic.register_opts(CONF)
+pci_devices.register_opts(CONF)
+processing.register_opts(CONF)
+pxe_filter.register_opts(CONF)
+swift.register_opts(CONF)
diff --git a/ironic_inspector/conf/capabilities.py b/ironic_inspector/conf/capabilities.py
new file mode 100644
index 000000000..82ac85fc3
--- /dev/null
+++ b/ironic_inspector/conf/capabilities.py
@@ -0,0 +1,45 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+DEFAULT_CPU_FLAGS_MAPPING = {
+    'vmx': 'cpu_vt',
+    'svm': 'cpu_vt',
+    'aes': 'cpu_aes',
+    'pse': 'cpu_hugepages',
+    'pdpe1gb': 'cpu_hugepages_1g',
+    'smx': 'cpu_txt',
+}
+
+
+_OPTS = [
+    cfg.BoolOpt('boot_mode',
+                default=False,
+                help=_('Whether to store the boot mode (BIOS or UEFI).')),
+    cfg.DictOpt('cpu_flags',
+                default=DEFAULT_CPU_FLAGS_MAPPING,
+                help=_('Mapping between a CPU flag and a capability to set '
+                       'if this flag is present.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'capabilities')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/default.py b/ironic_inspector/conf/default.py
new file mode 100644
index 000000000..a9efc6b5d
--- /dev/null
+++ b/ironic_inspector/conf/default.py
@@ -0,0 +1,81 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.StrOpt('listen_address',
+               default='0.0.0.0',
+               help=_('IP to listen on.')),
+    cfg.PortOpt('listen_port',
+                default=5050,
+                help=_('Port to listen on.')),
+    cfg.StrOpt('auth_strategy',
+               default='keystone',
+               choices=('keystone', 'noauth'),
+               help=_('Authentication method used on the ironic-inspector '
+                      'API. Either "noauth" or "keystone" are currently valid '
+                      'options. "noauth" will disable all authentication.')),
+    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=0,
+               help=_('For how much time (in seconds) to keep status '
+                      'information about nodes after introspection was '
+                      'finished for them. Set to 0 (the default) '
+                      'to disable the timeout.'),
+               deprecated_for_removal=True),
+    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('use_ssl',
+                default=False,
+                help=_('SSL Enabled/Disabled')),
+    cfg.StrOpt('ssl_cert_path',
+               default='',
+               help=_('Path to SSL certificate')),
+    cfg.StrOpt('ssl_key_path',
+               default='',
+               help=_('Path to SSL key')),
+    cfg.IntOpt('max_concurrency',
+               default=1000, min=2,
+               help=_('The green thread pool size.')),
+    cfg.IntOpt('introspection_delay',
+               default=5,
+               help=_('Delay (in seconds) between two introspections.')),
+    cfg.ListOpt('ipmi_address_fields',
+                default=['ilo_address', 'drac_host', 'drac_address',
+                         'cimc_address'],
+                help=_('Ironic driver_info fields that are equivalent '
+                       'to ipmi_address.')),
+    cfg.StrOpt('rootwrap_config',
+               default="/etc/ironic-inspector/rootwrap.conf",
+               help=_('Path to the rootwrap configuration file to use for '
+                      'running commands as root')),
+    cfg.IntOpt('api_max_limit', default=1000, min=1,
+               help=_('Limit the number of elements an API list-call returns'))
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS)
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/discovery.py b/ironic_inspector/conf/discovery.py
new file mode 100644
index 000000000..296caa002
--- /dev/null
+++ b/ironic_inspector/conf/discovery.py
@@ -0,0 +1,32 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.StrOpt('enroll_node_driver',
+               default='fake',
+               help=_('The name of the Ironic driver used by the enroll '
+                      'hook when creating a new node in Ironic.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'discovery')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/dnsmasq_pxe_filter.py b/ironic_inspector/conf/dnsmasq_pxe_filter.py
new file mode 100644
index 000000000..53b83890a
--- /dev/null
+++ b/ironic_inspector/conf/dnsmasq_pxe_filter.py
@@ -0,0 +1,48 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.StrOpt('dhcp_hostsdir',
+               default='/var/lib/ironic-inspector/dhcp-hostsdir',
+               help=_('The MAC address cache directory, exposed to dnsmasq.'
+                      'This directory is expected to be in exclusive control '
+                      'of the driver.')),
+    cfg.BoolOpt('purge_dhcp_hostsdir', default=True,
+                help=_('Purge the hostsdir upon driver initialization. '
+                       'Setting to false should only be performed when the '
+                       'deployment of inspector is such that there are '
+                       'multiple processes executing inside of the same host '
+                       'and namespace. In this case, the Operator is '
+                       'responsible for setting up a custom cleaning '
+                       'facility.')),
+    cfg.StrOpt('dnsmasq_start_command', default='',
+               help=_('A (shell) command line to start the dnsmasq service '
+                      'upon filter initialization. Default: don\'t start.')),
+    cfg.StrOpt('dnsmasq_stop_command', default='',
+               help=_('A (shell) command line to stop the dnsmasq service '
+                      'upon inspector (error) exit. Default: don\'t stop.')),
+
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'dnsmasq_pxe_filter')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/iptables.py b/ironic_inspector/conf/iptables.py
new file mode 100644
index 000000000..ea0e0859d
--- /dev/null
+++ b/ironic_inspector/conf/iptables.py
@@ -0,0 +1,60 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.BoolOpt('manage_firewall',
+                default=True,
+                # NOTE(milan) this filter driver will be replaced by
+                # a dnsmasq filter driver
+                deprecated_for_removal=True,
+                deprecated_group='firewall',
+                help=_('Whether to manage firewall rules for PXE port. '
+                       'This configuration option was deprecated in favor of '
+                       'the ``driver`` option in the ``pxe_filter`` section. '
+                       'Please, use the ``noop`` filter driver to disable the '
+                       'firewall filtering or the ``iptables`` filter driver '
+                       'to enable it.')),
+    cfg.StrOpt('dnsmasq_interface',
+               default='br-ctlplane',
+               deprecated_group='firewall',
+               help=_('Interface on which dnsmasq listens, the default is for '
+                      'VM\'s.')),
+    cfg.StrOpt('firewall_chain',
+               default='ironic-inspector',
+               deprecated_group='firewall',
+               help=_('iptables chain name to use.')),
+    cfg.ListOpt('ethoib_interfaces',
+                deprecated_group='firewall',
+                default=[],
+                help=_('List of Etherent Over InfiniBand interfaces '
+                       'on the Inspector host which are used for physical '
+                       'access to the DHCP network. Multiple interfaces would '
+                       'be attached to a bond or bridge specified in '
+                       'dnsmasq_interface. The MACs of the InfiniBand nodes '
+                       'which are not in desired state are going to be '
+                       'blacklisted based on the list of neighbor MACs '
+                       'on these interfaces.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'iptables')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/ironic.py b/ironic_inspector/conf/ironic.py
new file mode 100644
index 000000000..ea26371cc
--- /dev/null
+++ b/ironic_inspector/conf/ironic.py
@@ -0,0 +1,59 @@
+# 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 ironic_inspector.common.i18n import _
+from ironic_inspector.common import keystone
+
+
+IRONIC_GROUP = 'ironic'
+
+
+_OPTS = [
+    cfg.StrOpt('os_region',
+               help=_('Keystone region used to get Ironic endpoints.')),
+    cfg.StrOpt('auth_strategy',
+               default='keystone',
+               choices=('keystone', 'noauth'),
+               help=_('Method to use for authentication: noauth or '
+                      'keystone.')),
+    cfg.StrOpt('ironic_url',
+               default='http://localhost:6385/',
+               help=_('Ironic API URL, used to set Ironic API URL when '
+                      'auth_strategy option is noauth to work with standalone '
+                      'Ironic without keystone.')),
+    cfg.StrOpt('os_service_type',
+               default='baremetal',
+               help=_('Ironic service type.')),
+    cfg.StrOpt('os_endpoint_type',
+               default='internalURL',
+               help=_('Ironic endpoint type.')),
+    cfg.IntOpt('retry_interval',
+               default=2,
+               help=_('Interval between retries in case of conflict error '
+                      '(HTTP 409).')),
+    cfg.IntOpt('max_retries',
+               default=30,
+               help=_('Maximum number of retries in case of conflict error '
+                      '(HTTP 409).')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, IRONIC_GROUP)
+    keystone.register_auth_opts(IRONIC_GROUP)
+
+
+def list_opts():
+    return keystone.add_auth_options(_OPTS)
diff --git a/ironic_inspector/conf/opts.py b/ironic_inspector/conf/opts.py
new file mode 100644
index 000000000..d480f1a33
--- /dev/null
+++ b/ironic_inspector/conf/opts.py
@@ -0,0 +1,74 @@
+# 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 oslo_log import log
+from oslo_middleware import cors
+
+import ironic_inspector.conf
+from ironic_inspector import version
+
+
+MIN_VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Minimum-Version'
+MAX_VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Maximum-Version'
+VERSION_HEADER = 'X-OpenStack-Ironic-Inspector-API-Version'
+
+
+def set_config_defaults():
+    """Return a list of oslo.config options available in Inspector code."""
+    log.set_defaults(default_log_levels=['sqlalchemy=WARNING',
+                                         'iso8601=WARNING',
+                                         'requests=WARNING',
+                                         'urllib3.connectionpool=WARNING',
+                                         'keystonemiddleware=WARNING',
+                                         'swiftclient=WARNING',
+                                         'keystoneauth=WARNING',
+                                         'ironicclient=WARNING'])
+    set_cors_middleware_defaults()
+
+
+def set_cors_middleware_defaults():
+    """Update default configuration options for oslo.middleware."""
+    # TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/
+    cfg.set_defaults(
+        cors.CORS_OPTS,
+        allow_headers=['X-Auth-Token',
+                       MIN_VERSION_HEADER,
+                       MAX_VERSION_HEADER,
+                       VERSION_HEADER],
+        allow_methods=['GET', 'POST', 'PUT', 'HEAD',
+                       'PATCH', 'DELETE', 'OPTIONS']
+    )
+
+
+def parse_args(args, default_config_files=None):
+    cfg.CONF(args,
+             project='ironic-inspector',
+             version=version.version_info.release_string(),
+             default_config_files=default_config_files)
+
+
+def list_opts():
+    return [
+        ('capabilities', ironic_inspector.conf.capabilities.list_opts()),
+        ('DEFAULT', ironic_inspector.conf.default.list_opts()),
+        ('discovery', ironic_inspector.conf.discovery.list_opts()),
+        ('dnsmasq_pxe_filter',
+         ironic_inspector.conf.dnsmasq_pxe_filter.list_opts()),
+        ('swift', ironic_inspector.conf.swift.list_opts()),
+        ('ironic', ironic_inspector.conf.ironic.list_opts()),
+        ('iptables', ironic_inspector.conf.iptables.list_opts()),
+        ('processing', ironic_inspector.conf.processing.list_opts()),
+        ('pci_devices', ironic_inspector.conf.pci_devices.list_opts()),
+        ('pxe_filter', ironic_inspector.conf.pxe_filter.list_opts()),
+    ]
diff --git a/ironic_inspector/conf/pci_devices.py b/ironic_inspector/conf/pci_devices.py
new file mode 100644
index 000000000..2951d41a5
--- /dev/null
+++ b/ironic_inspector/conf/pci_devices.py
@@ -0,0 +1,34 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.MultiStrOpt('alias',
+                    default=[],
+                    help=_('An alias for PCI device identified by '
+                           '\'vendor_id\' and \'product_id\' fields. Format: '
+                           '{"vendor_id": "1234", "product_id": "5678", '
+                           '"name": "pci_dev1"}')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, group='pci_devices')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/processing.py b/ironic_inspector/conf/processing.py
new file mode 100644
index 000000000..9840f47cd
--- /dev/null
+++ b/ironic_inspector/conf/processing.py
@@ -0,0 +1,109 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+VALID_ADD_PORTS_VALUES = ('all', 'active', 'pxe', 'disabled')
+VALID_KEEP_PORTS_VALUES = ('all', 'present', 'added')
+VALID_STORE_DATA_VALUES = ('none', 'swift')
+
+
+_OPTS = [
+    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.StrOpt('keep_ports',
+               default='all',
+               help=_('Which ports (already present on a node) to keep after '
+                      'introspection. Possible values: all (do not delete '
+                      'anything), present (keep ports which MACs were present '
+                      'in introspection data), added (keep only MACs that we '
+                      'added during introspection).'),
+               choices=VALID_KEEP_PORTS_VALUES),
+    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.StrOpt('default_processing_hooks',
+               default='ramdisk_error,root_disk_selection,scheduler,'
+                       'validate_interfaces,capabilities,pci_devices',
+               help=_('Comma-separated list of default 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.StrOpt('processing_hooks',
+               default='$default_processing_hooks',
+               help=_('Comma-separated list of enabled hooks for processing '
+                      'pipeline. The default for this is '
+                      '$default_processing_hooks, hooks can be added before '
+                      'or after the defaults like this: '
+                      '"prehook,$default_processing_hooks,posthook".')),
+    cfg.StrOpt('ramdisk_logs_dir',
+               help=_('If set, logs from ramdisk will be stored in this '
+                      'directory.')),
+    cfg.BoolOpt('always_store_ramdisk_logs',
+                default=False,
+                help=_('Whether to store ramdisk logs even if it did not '
+                       'return an error message (dependent upon '
+                       '"ramdisk_logs_dir" option being set).')),
+    cfg.StrOpt('node_not_found_hook',
+               help=_('The name of the hook to run when inspector receives '
+                      'inspection information from a node it isn\'t already '
+                      'aware of. This hook is ignored by default.')),
+    cfg.StrOpt('store_data',
+               default='none',
+               choices=VALID_STORE_DATA_VALUES,
+               help=_('Method for storing introspection data. If set to \'none'
+                      '\', introspection data will not be stored.')),
+    cfg.StrOpt('store_data_location',
+               help=_('Name of the key to store the location of stored data '
+                      'in the extra column of the Ironic database.')),
+    cfg.BoolOpt('disk_partitioning_spacing',
+                default=True,
+                help=_('Whether to leave 1 GiB of disk size untouched for '
+                       'partitioning. Only has effect when used with the IPA '
+                       'as a ramdisk, for older ramdisk local_gb is '
+                       'calculated on the ramdisk side.')),
+    cfg.StrOpt('ramdisk_logs_filename_format',
+               default='{uuid}_{dt:%Y%m%d-%H%M%S.%f}.tar.gz',
+               help=_('File name template for storing ramdisk logs. The '
+                      'following replacements can be used: '
+                      '{uuid} - node UUID or "unknown", '
+                      '{bmc} - node BMC address or "unknown", '
+                      '{dt} - current UTC date and time, '
+                      '{mac} - PXE booting MAC or "unknown".')),
+    cfg.BoolOpt('power_off',
+                default=True,
+                help=_('Whether to power off a node after introspection.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'processing')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/pxe_filter.py b/ironic_inspector/conf/pxe_filter.py
new file mode 100644
index 000000000..b095720a5
--- /dev/null
+++ b/ironic_inspector/conf/pxe_filter.py
@@ -0,0 +1,35 @@
+# 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 ironic_inspector.common.i18n import _
+
+
+_OPTS = [
+    cfg.StrOpt('driver', default='iptables',
+               help=_('PXE boot filter driver to use, such as iptables')),
+    cfg.IntOpt('sync_period', default=15, min=0,
+               deprecated_name='firewall_update_period',
+               deprecated_group='firewall',
+               help=_('Amount of time in seconds, after which repeat periodic '
+                      'update of the filter.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, 'pxe_filter')
+
+
+def list_opts():
+    return _OPTS
diff --git a/ironic_inspector/conf/swift.py b/ironic_inspector/conf/swift.py
new file mode 100644
index 000000000..d6c5f66c9
--- /dev/null
+++ b/ironic_inspector/conf/swift.py
@@ -0,0 +1,54 @@
+# 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 ironic_inspector.common.i18n import _
+from ironic_inspector.common import keystone
+
+
+SWIFT_GROUP = 'swift'
+
+
+_OPTS = [
+    cfg.IntOpt('max_retries',
+               default=2,
+               help=_('Maximum number of times to retry a Swift request, '
+                      'before failing.')),
+    cfg.IntOpt('delete_after',
+               default=0,
+               help=_('Number of seconds that the Swift object will last '
+                      'before being deleted. (set to 0 to never delete the '
+                      'object).')),
+    cfg.StrOpt('container',
+               default='ironic-inspector',
+               help=_('Default Swift container to use when creating '
+                      'objects.')),
+    cfg.StrOpt('os_service_type',
+               default='object-store',
+               help=_('Swift service type.')),
+    cfg.StrOpt('os_endpoint_type',
+               default='internalURL',
+               help=_('Swift endpoint type.')),
+    cfg.StrOpt('os_region',
+               help=_('Keystone region to get endpoint for.')),
+]
+
+
+def register_opts(conf):
+    conf.register_opts(_OPTS, SWIFT_GROUP)
+    keystone.register_auth_opts(SWIFT_GROUP)
+
+
+def list_opts():
+    return keystone.add_auth_options(_OPTS)
diff --git a/ironic_inspector/main.py b/ironic_inspector/main.py
index d186a7b92..8c508dff2 100644
--- a/ironic_inspector/main.py
+++ b/ironic_inspector/main.py
@@ -16,7 +16,6 @@ import os
 import re
 
 import flask
-from oslo_config import cfg
 from oslo_utils import uuidutils
 import werkzeug
 
@@ -25,14 +24,15 @@ from ironic_inspector.common import context
 from ironic_inspector.common.i18n import _
 from ironic_inspector.common import ironic as ir_utils
 from ironic_inspector.common import swift
-from ironic_inspector import conf  # noqa
+import ironic_inspector.conf
+from ironic_inspector.conf import opts as conf_opts
 from ironic_inspector import introspect
 from ironic_inspector import node_cache
 from ironic_inspector import process
 from ironic_inspector import rules
 from ironic_inspector import utils
 
-CONF = cfg.CONF
+CONF = ironic_inspector.conf.CONF
 
 
 app = flask.Flask(__name__)
@@ -45,7 +45,7 @@ _LOGGING_EXCLUDED_KEYS = ('logs',)
 
 
 def _get_version():
-    ver = flask.request.headers.get(conf.VERSION_HEADER,
+    ver = flask.request.headers.get(conf_opts.VERSION_HEADER,
                                     _DEFAULT_API_VERSION)
     try:
         requested = tuple(int(x) for x in ver.split('.'))
@@ -103,8 +103,8 @@ def check_api_version():
 
 @app.after_request
 def add_version_headers(res):
-    res.headers[conf.MIN_VERSION_HEADER] = '%s.%s' % MINIMUM_API_VERSION
-    res.headers[conf.MAX_VERSION_HEADER] = '%s.%s' % CURRENT_API_VERSION
+    res.headers[conf_opts.MIN_VERSION_HEADER] = '%s.%s' % MINIMUM_API_VERSION
+    res.headers[conf_opts.MAX_VERSION_HEADER] = '%s.%s' % CURRENT_API_VERSION
     return res
 
 
diff --git a/ironic_inspector/plugins/capabilities.py b/ironic_inspector/plugins/capabilities.py
index a75171d9c..9725c4f4f 100644
--- a/ironic_inspector/plugins/capabilities.py
+++ b/ironic_inspector/plugins/capabilities.py
@@ -15,38 +15,11 @@
 
 from oslo_config import cfg
 
-from ironic_inspector.common.i18n import _
 from ironic_inspector.plugins import base
 from ironic_inspector import utils
 
 
-DEFAULT_CPU_FLAGS_MAPPING = {
-    'vmx': 'cpu_vt',
-    'svm': 'cpu_vt',
-    'aes': 'cpu_aes',
-    'pse': 'cpu_hugepages',
-    'pdpe1gb': 'cpu_hugepages_1g',
-    'smx': 'cpu_txt',
-}
-
-CAPABILITIES_OPTS = [
-    cfg.BoolOpt('boot_mode',
-                default=False,
-                help=_('Whether to store the boot mode (BIOS or UEFI).')),
-    cfg.DictOpt('cpu_flags',
-                default=DEFAULT_CPU_FLAGS_MAPPING,
-                help=_('Mapping between a CPU flag and a capability to set '
-                       'if this flag is present.')),
-]
-
-
-def list_opts():
-    return [
-        ('capabilities', CAPABILITIES_OPTS)
-    ]
-
 CONF = cfg.CONF
-CONF.register_opts(CAPABILITIES_OPTS, group='capabilities')
 LOG = utils.getProcessingLogger(__name__)
 
 
diff --git a/ironic_inspector/plugins/discovery.py b/ironic_inspector/plugins/discovery.py
index 0b32bc62a..9b65251a3 100644
--- a/ironic_inspector/plugins/discovery.py
+++ b/ironic_inspector/plugins/discovery.py
@@ -21,21 +21,7 @@ from ironic_inspector import node_cache
 from ironic_inspector import utils
 
 
-DISCOVERY_OPTS = [
-    cfg.StrOpt('enroll_node_driver',
-               default='fake',
-               help=_('The name of the Ironic driver used by the enroll '
-                      'hook when creating a new node in Ironic.')),
-]
-
-
-def list_opts():
-    return [
-        ('discovery', DISCOVERY_OPTS)
-    ]
-
 CONF = cfg.CONF
-CONF.register_opts(DISCOVERY_OPTS, group='discovery')
 
 LOG = utils.getProcessingLogger(__name__)
 
diff --git a/ironic_inspector/test/base.py b/ironic_inspector/test/base.py
index 887098866..bd97b272f 100644
--- a/ironic_inspector/test/base.py
+++ b/ironic_inspector/test/base.py
@@ -25,7 +25,8 @@ from oslo_utils import uuidutils
 from oslotest import base as test_base
 
 from ironic_inspector.common import i18n
-from ironic_inspector import conf
+import ironic_inspector.conf
+from ironic_inspector.conf import opts as conf_opts
 from ironic_inspector import db
 from ironic_inspector import introspection_state as istate
 from ironic_inspector import node_cache
@@ -33,7 +34,7 @@ from ironic_inspector.plugins import base as plugins_base
 from ironic_inspector.test.unit import policy_fixture
 from ironic_inspector import utils
 
-CONF = conf.cfg.CONF
+CONF = ironic_inspector.conf.CONF
 
 
 class BaseTest(test_base.BaseTestCase):
@@ -64,7 +65,7 @@ class BaseTest(test_base.BaseTestCase):
         self.cfg.set_default('connection', "sqlite:///", group='database')
         self.cfg.set_default('slave_connection', None, group='database')
         self.cfg.set_default('max_retries', 10, group='database')
-        conf.parse_args([], default_config_files=[])
+        conf_opts.parse_args([], default_config_files=[])
         self.policy = self.useFixture(policy_fixture.PolicyFixture())
 
     def assertPatchEqual(self, expected, actual):
diff --git a/ironic_inspector/test/unit/test_keystone.py b/ironic_inspector/test/unit/test_keystone.py
index 3d9d4cf5d..14a7aa84e 100644
--- a/ironic_inspector/test/unit/test_keystone.py
+++ b/ironic_inspector/test/unit/test_keystone.py
@@ -49,8 +49,7 @@ class KeystoneTest(base.BaseTest):
         self.assertEqual(auth1, sess.auth)
 
     def test_add_auth_options(self):
-        group, opts = keystone.add_auth_options([], TESTGROUP)[0]
-        self.assertEqual(TESTGROUP, group)
+        opts = keystone.add_auth_options([])
         # check that there is no duplicates
         names = {o.dest for o in opts}
         self.assertEqual(len(names), len(opts))
diff --git a/ironic_inspector/test/unit/test_main.py b/ironic_inspector/test/unit/test_main.py
index 7da31883e..31b5c1743 100644
--- a/ironic_inspector/test/unit/test_main.py
+++ b/ironic_inspector/test/unit/test_main.py
@@ -16,11 +16,11 @@ import json
 import unittest
 
 import mock
-from oslo_config import cfg
 from oslo_utils import uuidutils
 
 from ironic_inspector.common import ironic as ir_utils
-from ironic_inspector import conf
+import ironic_inspector.conf
+from ironic_inspector.conf import opts as conf_opts
 from ironic_inspector import introspect
 from ironic_inspector import introspection_state as istate
 from ironic_inspector import main
@@ -32,7 +32,7 @@ from ironic_inspector import rules
 from ironic_inspector.test import base as test_base
 from ironic_inspector import utils
 
-CONF = cfg.CONF
+CONF = ironic_inspector.conf.CONF
 
 
 def _get_error(res):
@@ -431,7 +431,7 @@ class TestApiRules(BaseAPITest):
         create_mock.return_value = mock.Mock(spec=rules.IntrospectionRule,
                                              **{'as_dict.return_value': exp})
 
-        headers = {conf.VERSION_HEADER:
+        headers = {conf_opts.VERSION_HEADER:
                    main._format_version((1, 5))}
 
         res = self.app.post('/v1/rules', data=json.dumps(data),
@@ -510,9 +510,9 @@ class TestApiMisc(BaseAPITest):
 class TestApiVersions(BaseAPITest):
     def _check_version_present(self, res):
         self.assertEqual('%d.%d' % main.MINIMUM_API_VERSION,
-                         res.headers.get(conf.MIN_VERSION_HEADER))
+                         res.headers.get(conf_opts.MIN_VERSION_HEADER))
         self.assertEqual('%d.%d' % main.CURRENT_API_VERSION,
-                         res.headers.get(conf.MAX_VERSION_HEADER))
+                         res.headers.get(conf_opts.MAX_VERSION_HEADER))
 
     def test_root_endpoint(self):
         res = self.app.get("/")
@@ -578,14 +578,14 @@ class TestApiVersions(BaseAPITest):
             self.app.post('/v1/introspection/foobar'))
 
     def test_request_correct_version(self):
-        headers = {conf.VERSION_HEADER:
+        headers = {conf_opts.VERSION_HEADER:
                    main._format_version(main.CURRENT_API_VERSION)}
         self._check_version_present(self.app.get('/', headers=headers))
 
     def test_request_unsupported_version(self):
         bad_version = (main.CURRENT_API_VERSION[0],
                        main.CURRENT_API_VERSION[1] + 1)
-        headers = {conf.VERSION_HEADER:
+        headers = {conf_opts.VERSION_HEADER:
                    main._format_version(bad_version)}
         res = self.app.get('/', headers=headers)
         self._check_version_present(res)
diff --git a/setup.cfg b/setup.cfg
index a80001287..bbc730210 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -62,14 +62,9 @@ ironic_inspector.pxe_filter =
     iptables = ironic_inspector.pxe_filter.iptables:IptablesFilter
     noop = ironic_inspector.pxe_filter.base:NoopFilter
 oslo.config.opts =
-    ironic_inspector = ironic_inspector.conf:list_opts
-    ironic_inspector.common.ironic = ironic_inspector.common.ironic:list_opts
-    ironic_inspector.common.swift = ironic_inspector.common.swift:list_opts
-    ironic_inspector.plugins.discovery = ironic_inspector.plugins.discovery:list_opts
-    ironic_inspector.plugins.capabilities = ironic_inspector.plugins.capabilities:list_opts
-    ironic_inspector.plugins.pci_devices = ironic_inspector.plugins.pci_devices:list_opts
+    ironic_inspector = ironic_inspector.conf.opts:list_opts
 oslo.config.opts.defaults =
-    ironic_inspector = ironic_inspector.conf:set_config_defaults
+    ironic_inspector = ironic_inspector.conf.opts:set_config_defaults
 oslo.policy.enforcer =
     ironic_inspector = ironic_inspector.policy:get_oslo_policy_enforcer
 oslo.policy.policies =