Extend configurable variables and retrieve config data

Gate ``available`` state on remote end signalling availability.

Depends-On: I9eb5f8a6039b74288a395a584f844507448cdc23
Change-Id: I44a23549e4830d89ba3182250c40dfe395767fb0
This commit is contained in:
Frode Nordahl 2019-09-24 16:34:17 +02:00
parent 1ab760f201
commit 4f7dba3da8
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
3 changed files with 128 additions and 40 deletions

View File

@ -6,25 +6,46 @@ that charm.
# Usage
## States
## Flags and States
The interface provides the `{relation-name}.connected` and
`{relation_name}.available` states.
`{relation_name}.available` flags and states.
## neutron\_config\_data
The neutron\_config\_data property allows the charm author to introspect a
subset of the principle charm context values prior to applying the context
provided by this relation.
This enables the subordinate charm to make informed decisions about how it
should configure Neutron based on how the deployment is configured.
```python
@reactive.when('neutron-plugin-api-subordinate.connected')
def configure_principle():
api_principle = reactive.endpoint_from_flag(
'neutron-plugin-api-subordinate.connected')
if 'dns' in api_principle.neutron_config_data['extension_drivers']:
...
## configure\_plugin
The configure\_plugin method allows the following to be configured in the
principle charm:
* **neutron\_plugin**: Name of the plugin type eg 'ovs', 'odl' etc. This is not
currently used in the principle but should be set to
something representitve of the plugin type.
* **neutron\_plugin**: Name of the plugin type eg 'ovs', 'ovn' etc. This is not
currently used in the principle but should be set to
something representitve of the plugin type.
* **core\_plugin**: Value of core\_plugin to be set in neutron.conf
* **neutron\_plugin\_config**: File containing plugin config. This config file is
appended to the list of configs the neutron
services read on startup.
* **neutron\_plugin\_config**: File containing plugin config. This config file
is appended to the list of configs the neutron
services read on startup.
* **service\_plugins**: Value of service\_plugins to be set in neutron.conf
* **subordinate\_configuration**: Config to be inserted into a configuration file
that the principle manages.
* **subordinate\_configuration**: Config to be inserted into a configuration
file that the principle manages.
* **extension\_drivers**: Value of extension\_drivers to be set in
ml2\_conf.ini
* **neutron\_security\_groups**: Toggle whether the Neutron security group
feature should be enabled or not.
Request `foo = bar` is inserted into the `DEFAULT` section of neutron.conf
@ -43,11 +64,15 @@ def configure_principle(api_principle):
}
}
}
service_plugins = ','.join((
api_principle.neutron_config_data.get('service_plugins', ''),
'networking_ovn.l3.l3_ovn.OVNL3RouterPlugin'),
)
api_principle.configure_plugin(
neutron_plugin='odl',
neutron_plugin='ovn',
core_plugin='neutron.plugins.ml2.plugin.Ml2Plugin',
neutron_plugin_config='/etc/neutron/plugins/ml2/ml2_conf.ini',
service_plugins='router,firewall,lbaas,vpnaas,metering',
service_plugins=service_plugins,
subordinate_configuration=inject_config)
```

View File

@ -14,7 +14,8 @@ class NeutronPluginAPISubordinate(RelationBase):
def changed(self):
"""Set connected state"""
self.set_state('{relation_name}.connected')
self.set_state('{relation_name}.available')
if self.neutron_api_ready():
self.set_state('{relation_name}.available')
@hook(
'{provides:neutron-plugin-api-subordinate}-relation-{broken,departed}')
@ -23,36 +24,67 @@ class NeutronPluginAPISubordinate(RelationBase):
self.remove_state('{relation_name}.available')
self.remove_state('{relation_name}.connected')
@property
def neutron_config_data(self):
return json.loads(self.get_remote('neutron_config_data', "{}"))
def neutron_api_ready(self):
if self.get_remote('neutron-api-ready') == 'yes':
return True
return False
def configure_plugin(self, neutron_plugin, core_plugin=None,
neutron_plugin_config=None, service_plugins=None,
subordinate_configuration=None):
subordinate_configuration=None,
extension_drivers=None, mechanism_drivers=None,
tenant_network_types=None,
neutron_security_groups=None):
"""Send principle plugin information
:param neutron_plugin: str Neutron plugin name eg odl
:param core_plugin: str eg neutron.plugins.ml2.plugin.Ml2Plugin
:param neutron-plugin-config: str /etc/neutron/plugins/ml2/ml2_conf.ini
:param service-plugins str: Comma delimited list of service plugins eg
router,firewall,lbaas,vpnaas,metering
:param subordinate_configuration dict: Configuration for the principle
to inject into a configuration
file it is managing eg:
# Add sections and tuples to insert values into neutron-server's
# neutron.conf e.g.
# {
# "neutron-api": {
# "/etc/neutron/neutron.conf": {
# "sections": {
# 'DEFAULT': [
# ('key1', 'val1')
# ('key2', 'val2')
# ],
# 'agent': [
# ('key3', 'val3')
# ],
# }
# }
# }
# }
:param neutron_plugin: Neutron plugin name
:type neutron_plugin: str
:param core_plugin: Entry point eg neutron.plugins.ml2.plugin.Ml2Plugin
:type core_plugin: str
:param neutron-plugin-config: /etc/neutron/plugins/ml2/ml2_conf.ini
:type neutron-plugin-config: str
:param service-plugins: Comma delimited list of service plugins eg
router,firewall,lbaas,vpnaas,metering
:type service-plugins: str
:param subordinate_configuration: Configuration for the principle
to inject into a configuration
file it is managing eg:
# Add sections and tuples to insert values into neutron-server's
# neutron.conf e.g.
# {
# "neutron-api": {
# "/etc/neutron/neutron.conf": {
# "sections": {
# 'DEFAULT': [
# ('key1', 'val1')
# ('key2', 'val2')
# ],
# 'agent': [
# ('key3', 'val3')
# ],
# }
# }
# }
# }
:type subordinate_configuration:
Dict[str, Dict[
str Dict[
str, List[
Union[str, str]]]]]
:param extension_drivers: Extension drivers eg dns,port_security
:type extension_drivers: str
:param mechanism_drivers: Mechanism drivers eg openvswitch,hyperv
:type mechanism_drivers: str
:param tenant_network_types: Tenant network types eg local,gre
Note that this also configures
``type_drivers``
:type tenant_network_types: str
:param neutron_security_groups: 'true' to enable security groups
:type neutron_security_groups: str
"""
if subordinate_configuration is None:
subordinate_configuration = {}
@ -62,6 +94,10 @@ class NeutronPluginAPISubordinate(RelationBase):
'core-plugin': core_plugin,
'neutron-plugin-config': neutron_plugin_config,
'service-plugins': service_plugins,
'extension-drivers': extension_drivers,
'mechanism-drivers': mechanism_drivers,
'tenant-network-types': tenant_network_types,
'neutron-security-groups': neutron_security_groups,
'subordinate_configuration': json.dumps(subordinate_configuration),
}
conversation.set_remote(**relation_info)

View File

@ -109,6 +109,12 @@ class TestNeutronPluginApiSubordinateProvides(test_utils.PatchHelper):
conversation = mock.MagicMock()
self.patch_target('conversation', conversation)
self.patch_target('set_state')
self.patch_target('get_remote')
self.get_remote.return_value = None
self.target.changed()
self.set_state.assert_called_once_with('{relation_name}.connected')
self.set_state.reset_mock()
self.get_remote.return_value = 'yes'
self.target.changed()
self.set_state.assert_has_calls([
mock.call('{relation_name}.connected'),
@ -125,6 +131,18 @@ class TestNeutronPluginApiSubordinateProvides(test_utils.PatchHelper):
mock.call('{relation_name}.connected'),
])
def test_neutron_api_ready(self):
self.patch_target('get_remote')
self.get_remote.return_value = 'yes'
self.assertTrue(self.target.neutron_api_ready())
self.get_remote.return_value = None
self.assertFalse(self.target.neutron_api_ready())
def teat_neutron_config_data(self):
self.patch_target('get_remote')
self.get_remote = json.dumps({'k': 'v'})
self.assertEquals(self.target.neutron_config_data, {'k': 'v'})
def test_configure_plugin(self):
conversation = mock.MagicMock()
self.patch_target('conversation', conversation)
@ -132,13 +150,22 @@ class TestNeutronPluginApiSubordinateProvides(test_utils.PatchHelper):
'aCorePlugin',
'aNeutronPluginConfig',
'servicePlugins1,servicePlugin2',
{'aKey': 'aValue'})
{'aKey': 'aValue'},
'extensionDriver1,extensionDriver2',
'mechanismDriver1,mechanismDriver2',
'typeDriver1,typeDriver2',
'toggleSecurityGroups',
)
conversation.set_remote.assert_called_once_with(
**{
'core-plugin': 'aCorePlugin',
'neutron-plugin': 'aPlugin',
'neutron-plugin-config': 'aNeutronPluginConfig',
'service-plugins': 'servicePlugins1,servicePlugin2',
'extension-drivers': 'extensionDriver1,extensionDriver2',
'mechanism-drivers': 'mechanismDriver1,mechanismDriver2',
'tenant-network-types': 'typeDriver1,typeDriver2',
'neutron-security-groups': 'toggleSecurityGroups',
'subordinate_configuration': json.dumps({'aKey': 'aValue'})},
)