Merge "Add support for Infoblox IPAM configuration via subordinate charm."

This commit is contained in:
Zuul 2019-06-21 14:14:23 +00:00 committed by Gerrit Code Review
commit 6d124b69cc
16 changed files with 226 additions and 4 deletions

View File

@ -0,0 +1 @@
neutron_api_hooks.py

View File

@ -0,0 +1 @@
neutron_api_hooks.py

View File

@ -0,0 +1 @@
neutron_api_hooks.py

View File

@ -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)

View File

@ -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()

View File

@ -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'],

View File

@ -55,6 +55,9 @@ requires:
interface: midonet
external-dns:
interface: designate
infoblox-neutron:
interface: infoblox
scope: container
certificates:
interface: tls-certificates
peers:

View File

@ -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 -%}

View File

@ -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

View File

@ -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

View 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 }}

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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)

View File

@ -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))