Provide plugins an oslo_config group for their setup

The plugin implementations need to be isolated from the
entity that is using os-vif. Plugins should be allowed
to define their own custom config parameters, without
needing the user of os-vif to pass further options into
the os_vif.initialize() method.

This change introduces a CONFIG_OPTS attribute on the
Plugin class. This provides a list of oslo_config
options that are to be used by the plugin. os_vif will
register these options into a group 'os_vif_$PLUGIN'
eg 'os_vif_linux_bridge'.

This gives the plugin impls the ability to have config
options in the main project conf (ie /etc/nova/nova.conf)
without exposing them directly to all of nova's own
config options.

Change-Id: I9634b474cd0d3fda09aef5800020c9415c28e3d8
This commit is contained in:
Daniel P. Berrange 2016-02-01 17:44:09 +00:00
parent 1ff6526c3d
commit 72d5dfbf48
5 changed files with 89 additions and 50 deletions

View File

@ -14,19 +14,12 @@ Usage
----- -----
The interface to the `os_vif` library is very simple. To begin using the The interface to the `os_vif` library is very simple. To begin using the
library, first call the `os_vif.initialize()` function, supplying a set of library, first call the `os_vif.initialize()` function. This will load
keyword arguments for configuration options:: all installed plugins and register the object model::
import os_vif import os_vif
os_vif.initialize(libvirt_virt_type='kvm', os_vif.initialize()
network_device_mtu=1500,
vlan_interface='eth1',
use_ipv6=False,
iptables_top_regex='',
iptables_bottom_regex='',
iptables_drop_action='DROP',
forward_bridge_interface=['all'])
Once the `os_vif` library is initialized, there are only two other library Once the `os_vif` library is initialized, there are only two other library
functions: `os_vif.plug()` and `os_vif.unplug()`. Both methods accept a single functions: `os_vif.plug()` and `os_vif.unplug()`. Both methods accept a single

View File

@ -24,7 +24,7 @@ _EXT_MANAGER = None
LOG = logging.getLogger('os_vif') LOG = logging.getLogger('os_vif')
def initialize(reset=False, **config): def initialize(reset=False):
""" """
Loads all os_vif plugins and initializes them with a dictionary of Loads all os_vif plugins and initializes them with a dictionary of
configuration options. These configuration options are passed as-is configuration options. These configuration options are passed as-is
@ -32,38 +32,16 @@ def initialize(reset=False, **config):
:param reset: Recreate and load the VIF plugin extensions. :param reset: Recreate and load the VIF plugin extensions.
The following configuration options are currently known to be
used by the VIF plugins, however this list may change and you
should check the documentation of individual plugins for a complete
list of configuration options that the plugin understands or uses.
:param **config: Configuration option dictionary.
`use_ipv6`: Default: False. For plugins that configure IPv6 iptables
rules or functionality, set this option to True if you want
to support IPv6.
`disable_rootwrap`: Default: False. Set to True to force plugins to use
sudoers files instead of any `oslo.rootwrap` functionality.
`use_rootwrap_daemon`: Default: False. Set to True to use the optional
`oslo.rootwrap` daemon for better performance of root-run
commands.
`rootwrap_config`: Default: /etc/nova/rootwrap.conf. Path to the
rootwrap configuration file.
`iptables_top_regex`: Default: ''. Override top filters in iptables
rules construction.
`iptables_bottom_regex`: Default: ''. Override bottom filters in
iptables rules construction.
`iptables_drop_action`: Default: DROP. Override the name of the drop
action in iptables rules.
`forward_bridge_interface`: Default: ['all'].
`network_device_mtu`: Default: 1500. Override the MTU of network
devices created by a VIF plugin.
""" """
global _EXT_MANAGER global _EXT_MANAGER
if reset or (_EXT_MANAGER is None): if reset or (_EXT_MANAGER is None):
_EXT_MANAGER = extension.ExtensionManager(namespace='os_vif', _EXT_MANAGER = extension.ExtensionManager(namespace='os_vif',
invoke_on_load=True, invoke_on_load=False)
invoke_args=config) for plugin_name in _EXT_MANAGER.keys():
cls = _EXT_MANAGER[plugin_name].plugin
obj = cls.load(plugin_name)
_EXT_MANAGER[plugin_name].obj = obj
os_vif.objects.register_all() os_vif.objects.register_all()

View File

@ -11,10 +11,13 @@
# under the License. # under the License.
import abc import abc
from oslo_config import cfg
import six import six
CONF = cfg.CONF
class PluginVIFInfo(object): class PluginVIFInfo(object):
""" """
Class describing the plugin and the versions of VIF object it understands. Class describing the plugin and the versions of VIF object it understands.
@ -54,10 +57,15 @@ class PluginInfo(object):
class PluginBase(object): class PluginBase(object):
"""Base class for all VIF plugins.""" """Base class for all VIF plugins."""
def __init__(self, **config): # Override to provide a tuple of oslo_config.Opt instances for
# the plugin config parameters
CONFIG_OPTS = ()
def __init__(self, config):
""" """
Sets up the plugin using supplied kwargs representing configuration Initialize the plugin object with the provided config
options.
:param config: `oslo_config.ConfigOpts.GroupAttr` instance:
""" """
self.config = config self.config = config
@ -93,3 +101,24 @@ class PluginBase(object):
this method should let `processutils.ProcessExecutionError` this method should let `processutils.ProcessExecutionError`
bubble up. bubble up.
""" """
@classmethod
def load(cls, plugin_name):
"""
Load a plugin, registering its configuration options
:param plugin_name: the name of the plugin extension
:returns: an initialized instance of the class
"""
cfg_group_name = "os_vif_" + plugin_name
cfg_opts = getattr(cls, "CONFIG_OPTS")
cfg_vals = None
if cfg_opts and len(cfg_opts) > 0:
cfg_group = cfg.OptGroup(
cfg_group_name,
"os-vif plugin %s options" % plugin_name)
CONF.register_opts(cfg_opts, group=cfg_group)
cfg_vals = getattr(CONF, cfg_group_name)
return cls(cfg_vals)

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import mock import mock
from oslo_config import cfg
from stevedore import extension from stevedore import extension
import os_vif import os_vif
@ -22,13 +23,34 @@ from os_vif.tests import base
class DemoPlugin(plugin.PluginBase): class DemoPlugin(plugin.PluginBase):
CONFIG_OPTS = (
cfg.BoolOpt("make_it_work",
default=False,
help="Make everything work correctly by setting this"),
cfg.IntOpt("sleep_time",
default=0,
help="How long to artifically sleep")
)
def describe(self): def describe(self):
pass pass
def plug(self, vif, instance_info): def plug(self, vif, instance_info, config):
pass pass
def unplug(self, vif, instance_info): def unplug(self, vif, instance_info, config):
pass
class DemoPluginNoConfig(plugin.PluginBase):
def describe(self):
pass
def plug(self, vif, instance_info, config):
pass
def unplug(self, vif, instance_info, config):
pass pass
@ -46,9 +68,25 @@ class TestOSVIF(base.TestCase):
os_vif.initialize() os_vif.initialize()
os_vif.initialize() os_vif.initialize()
mock_EM.assert_called_once_with( mock_EM.assert_called_once_with(
invoke_args={}, invoke_on_load=True, namespace='os_vif') invoke_on_load=False, namespace='os_vif')
self.assertIsNotNone(os_vif._EXT_MANAGER) self.assertIsNotNone(os_vif._EXT_MANAGER)
def test_load_plugin(self):
obj = DemoPlugin.load("demo")
self.assertTrue(hasattr(cfg.CONF, "os_vif_demo"))
self.assertTrue(hasattr(cfg.CONF.os_vif_demo, "make_it_work"))
self.assertTrue(hasattr(cfg.CONF.os_vif_demo, "sleep_time"))
self.assertEqual(cfg.CONF.os_vif_demo.make_it_work, False)
self.assertEqual(cfg.CONF.os_vif_demo.sleep_time, 0)
self.assertEqual(obj.config, cfg.CONF.os_vif_demo)
def test_load_plugin_no_config(self):
obj = DemoPluginNoConfig.load("demonocfg")
self.assertFalse(hasattr(cfg.CONF, "os_vif_demonocfg"))
self.assertIsNone(obj.config)
def test_plug_not_initialized(self): def test_plug_not_initialized(self):
self.assertRaises( self.assertRaises(
exception.LibraryNotInitialized, exception.LibraryNotInitialized,
@ -63,8 +101,8 @@ class TestOSVIF(base.TestCase):
def test_plug(self, mock_plug): def test_plug(self, mock_plug):
plg = extension.Extension(name="demo", plg = extension.Extension(name="demo",
entry_point="os-vif", entry_point="os-vif",
plugin="DemoPlugin", plugin=DemoPlugin,
obj=DemoPlugin()) obj=None)
with mock.patch('stevedore.extension.ExtensionManager', with mock.patch('stevedore.extension.ExtensionManager',
return_value={'foobar': plg}): return_value={'foobar': plg}):
os_vif.initialize() os_vif.initialize()
@ -78,8 +116,8 @@ class TestOSVIF(base.TestCase):
def test_unplug(self, mock_unplug): def test_unplug(self, mock_unplug):
plg = extension.Extension(name="demo", plg = extension.Extension(name="demo",
entry_point="os-vif", entry_point="os-vif",
plugin="DemoPlugin", plugin=DemoPlugin,
obj=DemoPlugin()) obj=None)
with mock.patch('stevedore.extension.ExtensionManager', with mock.patch('stevedore.extension.ExtensionManager',
return_value={'foobar': plg}): return_value={'foobar': plg}):
os_vif.initialize() os_vif.initialize()

View File

@ -4,6 +4,7 @@
pbr>=1.6 pbr>=1.6
netaddr>=0.7.12,!=0.7.16 netaddr>=0.7.12,!=0.7.16
oslo.config>=3.4.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0 oslo.log>=1.14.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0
oslo.versionedobjects>=0.13.0 oslo.versionedobjects>=0.13.0