Add support for Infoblox IPAM configuration via subordinate charm.
This change adds infoblox-api relation which allows neutron-server to publish events to a remote infoblox server. Additionally this change enables IPAM for the neutron service, which forces neutron to authorize any network changes against the target Infoblox server. This change adds the proper hooks, context, and templates to add infobox configuration to /etc/neutron/neutron.conf, passed by the infoblox subordinate charm. Closes-Bug: 1776689 Change-Id: Ib11377bd61c2b3fed5104ba0a423073a15cc18a2
This commit is contained in:
parent
123f366fc5
commit
92a1062830
1
hooks/infoblox-neutron-relation-broken
Symbolic link
1
hooks/infoblox-neutron-relation-broken
Symbolic link
@ -0,0 +1 @@
|
||||
neutron_api_hooks.py
|
1
hooks/infoblox-neutron-relation-changed
Symbolic link
1
hooks/infoblox-neutron-relation-changed
Symbolic link
@ -0,0 +1 @@
|
||||
neutron_api_hooks.py
|
1
hooks/infoblox-neutron-relation-departed
Symbolic link
1
hooks/infoblox-neutron-relation-departed
Symbolic link
@ -0,0 +1 @@
|
||||
neutron_api_hooks.py
|
@ -879,3 +879,43 @@ class DesignateContext(context.OSContextGenerator):
|
||||
ctxt['ipv6_ptr_zone_prefix_size'] = (
|
||||
config('ipv6-ptr-zone-prefix-size'))
|
||||
return ctxt
|
||||
|
||||
|
||||
class NeutronInfobloxContext(context.OSContextGenerator):
|
||||
'''Infoblox IPAM context for Neutron API'''
|
||||
interfaces = ['infoblox-neutron']
|
||||
|
||||
def __call__(self):
|
||||
ctxt = {}
|
||||
rdata = {}
|
||||
for rid in relation_ids('infoblox-neutron'):
|
||||
if related_units(rid) and not rdata:
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
ctxt['cloud_data_center_id'] = rdata.get('dc_id')
|
||||
break
|
||||
if ctxt.get('cloud_data_center_id') is not None:
|
||||
if not self.check_requirements(rdata):
|
||||
log('Missing Infoblox connection information, passing.')
|
||||
return {}
|
||||
ctxt['enable_infoblox'] = True
|
||||
ctxt['cloud_data_center_id'] = rdata.get('dc_id')
|
||||
ctxt['grid_master_host'] = rdata.get('grid_master_host')
|
||||
ctxt['grid_master_name'] = rdata.get('grid_master_name')
|
||||
ctxt['infoblox_admin_user_name'] = rdata.get('admin_user_name')
|
||||
ctxt['infoblox_admin_password'] = rdata.get('admin_password')
|
||||
# the next three values are non-critical and may accept defaults
|
||||
ctxt['wapi_version'] = rdata.get('wapi_version', '2.3')
|
||||
ctxt['wapi_max_results'] = rdata.get('wapi_max_results', '-50000')
|
||||
ctxt['wapi_paging'] = rdata.get('wapi_paging', True)
|
||||
return ctxt
|
||||
|
||||
def check_requirements(self, rdata):
|
||||
required = [
|
||||
'grid_master_name',
|
||||
'grid_master_host',
|
||||
'admin_user_name',
|
||||
'admin_password',
|
||||
]
|
||||
return len(set(p for p, v in rdata.items() if v).
|
||||
intersection(required)) == len(required)
|
||||
|
@ -35,6 +35,7 @@ from charmhelpers.core.hookenv import (
|
||||
open_port,
|
||||
unit_get,
|
||||
related_units,
|
||||
is_leader,
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
@ -86,6 +87,7 @@ from neutron_api_utils import (
|
||||
pause_unit_helper,
|
||||
resume_unit_helper,
|
||||
remove_old_packages,
|
||||
is_db_initialised,
|
||||
)
|
||||
from neutron_api_context import (
|
||||
get_dns_domain,
|
||||
@ -294,6 +296,7 @@ def config_changed():
|
||||
packages_removed = remove_old_packages()
|
||||
configure_https()
|
||||
update_nrpe_config()
|
||||
infoblox_changed()
|
||||
CONFIGS.write_all()
|
||||
if packages_removed and not is_unit_paused_set():
|
||||
log("Package purge detected, restarting services", "INFO")
|
||||
@ -359,7 +362,7 @@ def db_changed():
|
||||
return
|
||||
CONFIGS.write_all()
|
||||
conditional_neutron_migration()
|
||||
|
||||
infoblox_changed()
|
||||
for r_id in relation_ids('neutron-plugin-api-subordinate'):
|
||||
neutron_plugin_api_subordinate_relation_joined(relid=r_id)
|
||||
|
||||
@ -415,6 +418,7 @@ def identity_changed():
|
||||
for r_id in relation_ids('neutron-plugin-api-subordinate'):
|
||||
neutron_plugin_api_subordinate_relation_joined(relid=r_id)
|
||||
configure_https()
|
||||
infoblox_changed()
|
||||
|
||||
|
||||
@hooks.hook('neutron-api-relation-joined')
|
||||
@ -655,6 +659,34 @@ def designate_changed():
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
||||
@hooks.hook('infoblox-neutron-relation-changed')
|
||||
@restart_on_change(restart_map)
|
||||
def infoblox_changed():
|
||||
# The neutron DB upgrade will add new tables to
|
||||
# neutron db related to infoblox service.
|
||||
# Please take a look to charm-infoblox docs.
|
||||
if 'infoblox-neutron' not in CONFIGS.complete_contexts():
|
||||
log('infoblox-neutron relation incomplete. Peer not ready?')
|
||||
return
|
||||
|
||||
CONFIGS.write(NEUTRON_CONF)
|
||||
|
||||
if is_leader():
|
||||
ready = False
|
||||
if is_db_initialised() and neutron_ready():
|
||||
migrate_neutron_database(upgrade=True)
|
||||
ready = True
|
||||
for rid in relation_ids('infoblox-neutron'):
|
||||
relation_set(relation_id=rid, neutron_api_ready=ready)
|
||||
|
||||
|
||||
@hooks.hook('infoblox-neutron-relation-departed',
|
||||
'infoblox-neutron-relation-broken')
|
||||
@restart_on_change(restart_map)
|
||||
def infoblox_departed():
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
||||
@hooks.hook('update-status')
|
||||
@harden()
|
||||
@harden()
|
||||
|
@ -173,7 +173,8 @@ BASE_RESOURCE_MAP = OrderedDict([
|
||||
context.WorkerConfigContext(),
|
||||
context.InternalEndpointContext(),
|
||||
context.MemcacheContext(),
|
||||
neutron_api_context.DesignateContext()],
|
||||
neutron_api_context.DesignateContext(),
|
||||
neutron_api_context.NeutronInfobloxContext()],
|
||||
}),
|
||||
(NEUTRON_DEFAULT, {
|
||||
'services': ['neutron-server'],
|
||||
|
@ -55,6 +55,9 @@ requires:
|
||||
interface: midonet
|
||||
external-dns:
|
||||
interface: designate
|
||||
infoblox-neutron:
|
||||
interface: infoblox
|
||||
scope: container
|
||||
certificates:
|
||||
interface: tls-certificates
|
||||
peers:
|
||||
|
@ -75,6 +75,10 @@ global_physnet_mtu = {{ global_physnet_mtu }}
|
||||
external_dns_driver = designate
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
ipam_driver = infoblox
|
||||
{% endif -%}
|
||||
|
||||
{% include "section-zeromq" %}
|
||||
|
||||
[quotas]
|
||||
@ -121,3 +125,7 @@ lock_path = $state_path/lock
|
||||
{% endif -%}
|
||||
|
||||
{% include "section-oslo-middleware" %}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
{% include "parts/section-infoblox" %}
|
||||
{% endif -%}
|
||||
|
@ -75,6 +75,10 @@ global_physnet_mtu = {{ global_physnet_mtu }}
|
||||
external_dns_driver = designate
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
ipam_driver = infoblox
|
||||
{% endif -%}
|
||||
|
||||
{% include "section-zeromq" %}
|
||||
|
||||
[quotas]
|
||||
@ -120,6 +124,10 @@ lock_path = $state_path/lock
|
||||
{% include "parts/section-designate" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
{% include "parts/section-infoblox" %}
|
||||
{% endif -%}
|
||||
|
||||
[service_providers]
|
||||
service_provider = LOADBALANCERV2:Haproxy:neutron_lbaas.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default
|
||||
service_provider = VPN:strongswan:neutron_vpnaas.services.vpn.service_drivers.ipsec.IPsecVPNDriver:default
|
||||
|
@ -78,6 +78,10 @@ global_physnet_mtu = {{ global_physnet_mtu }}
|
||||
external_dns_driver = designate
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
ipam_driver = infoblox
|
||||
{% endif -%}
|
||||
|
||||
{% include "parts/section-placement" %}
|
||||
|
||||
{% include "section-zeromq" %}
|
||||
@ -125,6 +129,10 @@ lock_path = $state_path/lock
|
||||
{% include "parts/section-designate" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
{% include "parts/section-infoblox" %}
|
||||
{% endif -%}
|
||||
|
||||
[service_providers]
|
||||
service_provider = LOADBALANCERV2:Haproxy:neutron_lbaas.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default
|
||||
service_provider = VPN:strongswan:neutron_vpnaas.services.vpn.service_drivers.ipsec.IPsecVPNDriver:default
|
||||
|
11
templates/parts/section-infoblox
Normal file
11
templates/parts/section-infoblox
Normal file
@ -0,0 +1,11 @@
|
||||
[infoblox]
|
||||
cloud_data_center_id = {{ cloud_data_center_id }}
|
||||
|
||||
[infoblox-dc:{{ cloud_data_center_id }}]
|
||||
grid_master_host = {{ grid_master_host }}
|
||||
grid_master_name = {{ grid_master_name }}
|
||||
admin_user_name = {{ infoblox_admin_user_name }}
|
||||
admin_password = {{ infoblox_admin_password }}
|
||||
wapi_version = {{ wapi_version }}
|
||||
wapi_max_results = {{ wapi_max_results }}
|
||||
wapi_paging = {{ wapi_paging }}
|
@ -1,5 +1,31 @@
|
||||
[nova]
|
||||
{% if enable_infoblox -%}
|
||||
# TODO - Exceptionally we added the content of [keystone_authtoken] due to an
|
||||
# internal mechanism of Infoblox plugin lp-1688039.
|
||||
{% if auth_host -%}
|
||||
auth_type = password
|
||||
{% if api_version == "3" -%}
|
||||
auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v3
|
||||
auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/v3
|
||||
project_domain_name = {{ admin_domain_name }}
|
||||
user_domain_name = {{ admin_domain_name }}
|
||||
{% else -%}
|
||||
auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}
|
||||
auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}
|
||||
project_domain_name = default
|
||||
user_domain_name = default
|
||||
{% endif -%}
|
||||
project_name = {{ admin_tenant_name }}
|
||||
username = {{ admin_user }}
|
||||
password = {{ admin_password }}
|
||||
signing_dir = {{ signing_dir }}
|
||||
{% if use_memcache == true %}
|
||||
memcached_servers = {{ memcache_url }}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% else %}
|
||||
auth_section = keystone_authtoken
|
||||
{% endif %}
|
||||
region_name = {{ region }}
|
||||
{% if use_internal_endpoints -%}
|
||||
endpoint_type = internal
|
||||
|
@ -78,6 +78,10 @@ global_physnet_mtu = {{ global_physnet_mtu }}
|
||||
external_dns_driver = designate
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
ipam_driver = infoblox
|
||||
{% endif -%}
|
||||
|
||||
{% include "section-zeromq" %}
|
||||
|
||||
[quotas]
|
||||
@ -123,6 +127,10 @@ lock_path = $state_path/lock
|
||||
{% include "parts/section-designate" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
{% include "parts/section-infoblox" %}
|
||||
{% endif -%}
|
||||
|
||||
{% include "parts/section-placement" %}
|
||||
|
||||
[service_providers]
|
||||
|
@ -78,6 +78,10 @@ global_physnet_mtu = {{ global_physnet_mtu }}
|
||||
external_dns_driver = designate
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
ipam_driver = infoblox
|
||||
{% endif -%}
|
||||
|
||||
{% include "section-zeromq" %}
|
||||
|
||||
[quotas]
|
||||
@ -123,6 +127,10 @@ lock_path = $state_path/lock
|
||||
{% include "parts/section-designate" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if enable_infoblox -%}
|
||||
{% include "parts/section-infoblox" %}
|
||||
{% endif -%}
|
||||
|
||||
{% include "parts/section-placement" %}
|
||||
|
||||
[service_providers]
|
||||
|
@ -1398,3 +1398,54 @@ class NeutronLoadBalancerContextTest(CharmTestCase):
|
||||
'base_url': 'http://1.2.3.4:1234'})
|
||||
with self.assertRaises(ValueError):
|
||||
context.NeutronLoadBalancerContext()()
|
||||
|
||||
|
||||
class NeutronInfobloxContextTest(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NeutronInfobloxContextTest, self).setUp(context, TO_PATCH)
|
||||
self.relation_get.side_effect = self.test_relation.get
|
||||
self.config.side_effect = self.test_config.get
|
||||
|
||||
def tearDown(self):
|
||||
super(NeutronInfobloxContextTest, self).tearDown()
|
||||
|
||||
def test_infoblox_no_related_units(self):
|
||||
self.related_units.return_value = []
|
||||
ctxt = context.NeutronInfobloxContext()()
|
||||
expect = {}
|
||||
|
||||
self.assertEqual(expect, ctxt)
|
||||
|
||||
def test_infoblox_related_units(self):
|
||||
self.related_units.return_value = ['unit1']
|
||||
self.relation_ids.return_value = ['rid1']
|
||||
self.test_relation.set(
|
||||
{'dc_id': '0',
|
||||
'grid_master_host': 'foo',
|
||||
'grid_master_name': 'bar',
|
||||
'admin_user_name': 'faz',
|
||||
'admin_password': 'baz'})
|
||||
ctxt = context.NeutronInfobloxContext()()
|
||||
expect = {'enable_infoblox': True,
|
||||
'cloud_data_center_id': '0',
|
||||
'grid_master_host': 'foo',
|
||||
'grid_master_name': 'bar',
|
||||
'infoblox_admin_user_name': 'faz',
|
||||
'infoblox_admin_password': 'baz',
|
||||
'wapi_version': '2.3',
|
||||
'wapi_max_results': '-50000',
|
||||
'wapi_paging': True}
|
||||
|
||||
self.assertEqual(expect, ctxt)
|
||||
|
||||
def test_infoblox_related_units_missing_data(self):
|
||||
self.related_units.return_value = ['unit1']
|
||||
self.relation_ids.return_value = ['rid1']
|
||||
self.test_relation.set(
|
||||
{'dc_id': '0',
|
||||
'grid_master_host': 'foo'})
|
||||
ctxt = context.NeutronInfobloxContext()()
|
||||
expect = {}
|
||||
|
||||
self.assertEqual(expect, ctxt)
|
||||
|
@ -64,6 +64,7 @@ TO_PATCH = [
|
||||
'get_l2population',
|
||||
'get_overlay_network_type',
|
||||
'is_clustered',
|
||||
'is_leader',
|
||||
'is_elected_leader',
|
||||
'is_qos_requested_and_valid',
|
||||
'is_vlan_trunking_requested_and_valid',
|
||||
@ -90,7 +91,7 @@ TO_PATCH = [
|
||||
'remove_old_packages',
|
||||
'services',
|
||||
'service_restart',
|
||||
'generate_ha_relation_data',
|
||||
'is_db_initialised',
|
||||
]
|
||||
NEUTRON_CONF_DIR = "/etc/neutron"
|
||||
|
||||
@ -814,7 +815,7 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
self.is_elected_leader.return_value = True
|
||||
self.os_release.return_value = 'kilo'
|
||||
hooks.conditional_neutron_migration()
|
||||
self.migrate_neutron_database.assert_called_with()
|
||||
self.migrate_neutron_database.assert_called()
|
||||
|
||||
def test_conditional_neutron_migration_leader_icehouse(self):
|
||||
self.test_relation.set({
|
||||
@ -849,3 +850,17 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
def test_designate_peer_departed(self):
|
||||
self._call_hook('external-dns-relation-departed')
|
||||
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
|
||||
|
||||
def test_infoblox_peer_changed(self):
|
||||
self.is_db_initialised.return_value = True
|
||||
self.test_relation.set({
|
||||
'dc_id': '0',
|
||||
})
|
||||
self.os_release.return_value = 'queens'
|
||||
self.relation_ids.side_effect = self._fake_relids
|
||||
self._call_hook('infoblox-neutron-relation-changed')
|
||||
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
|
||||
|
||||
def test_infoblox_peer_departed(self):
|
||||
self._call_hook('infoblox-neutron-relation-departed')
|
||||
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
|
||||
|
Loading…
Reference in New Issue
Block a user