[project-calico,r=james-page] Add support for Calico plugin

This commit is contained in:
James Page 2015-09-04 12:03:14 +01:00
commit f87ad07e29
18 changed files with 251 additions and 4 deletions

View File

@ -99,6 +99,7 @@ options:
.
ovs - OpenvSwitch Plugin
nsx - VMWare NSX
Calico - Project Calico Networking
.
overlay-network-type:
default: gre
@ -388,3 +389,12 @@ options:
description: |
If True neutron-server will install neutron packages for the plugin
configured.
# Calico plugin configuration
calico-origin:
default:
type: string
description: |
Repository from which to install Calico packages. If set, must be
a PPA URL, of the form ppa:somecustom/ppa. Changing this value
after installation will force an immediate software upgrade.
# End of Calico plugin configuration

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

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

View File

@ -240,6 +240,28 @@ class HAProxyContext(context.HAProxyContext):
return ctxt
class EtcdContext(context.OSContextGenerator):
interfaces = ['etcd-proxy']
def __call__(self):
ctxt = {'cluster': ''}
cluster_string = ''
if not config('neutron-plugin') == 'Calico':
return ctxt
for rid in relation_ids('etcd-proxy'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
cluster_string = rdata.get('cluster')
if cluster_string:
break
ctxt['cluster'] = cluster_string
return ctxt
class NeutronApiSDNContext(context.SubordinateConfigContext):
interfaces = 'neutron-plugin-api-subordinate'

View File

@ -60,6 +60,8 @@ from neutron_api_utils import (
services,
setup_ipv6,
get_topics,
additional_install_locations,
force_etcd_restart,
)
from neutron_api_context import (
get_dvr,
@ -67,6 +69,7 @@ from neutron_api_context import (
get_l2population,
get_overlay_network_type,
IdentityServiceContext,
EtcdContext,
)
from charmhelpers.contrib.hahelpers.cluster import (
@ -143,6 +146,9 @@ def configure_https():
def install():
execd_preinstall()
configure_installation_source(config('openstack-origin'))
additional_install_locations(
config('neutron-plugin'), config('openstack-origin')
)
apt_update()
apt_install(determine_packages(config('openstack-origin')),
@ -183,6 +189,10 @@ def config_changed():
if openstack_upgrade_available('neutron-common'):
do_openstack_upgrade(CONFIGS)
additional_install_locations(
config('neutron-plugin'),
config('openstack-origin')
)
apt_install(filter_installed_packages(
determine_packages(config('openstack-origin'))),
fatal=True)
@ -352,6 +362,7 @@ def neutron_plugin_api_relation_joined(rid=None):
'enable-dvr': get_dvr(),
'enable-l3ha': get_l3ha(),
'overlay-network-type': get_overlay_network_type(),
'addr': unit_get('private-address'),
}
# Provide this value to relations since it needs to be set in multiple
@ -500,6 +511,20 @@ def update_nrpe_config():
nrpe_setup.write()
@hooks.hook('etcd-proxy-relation-joined')
@hooks.hook('etcd-proxy-relation-changed')
def etcd_proxy_force_restart(relation_id=None):
# note(cory.benfield): Mostly etcd does not require active management,
# but occasionally it does require a full config nuking. This does not
# play well with the standard neutron-api config management, so we
# treat etcd like the special snowflake it insists on being.
CONFIGS.register('/etc/init/etcd.conf', [EtcdContext()])
CONFIGS.write('/etc/init/etcd.conf')
if 'etcd-proxy' in CONFIGS.complete_contexts():
force_etcd_restart()
def main():
try:
hooks.execute(sys.argv)

View File

@ -4,6 +4,7 @@ from functools import partial
import os
import shutil
import subprocess
import glob
from base64 import b64encode
from charmhelpers.contrib.openstack import context, templating
from charmhelpers.contrib.openstack.neutron import (
@ -38,11 +39,13 @@ from charmhelpers.fetch import (
)
from charmhelpers.core.host import (
lsb_release,
adduser,
add_group,
add_user_to_group,
mkdir,
lsb_release,
service_stop,
service_start,
service_restart,
write_file,
)
@ -160,6 +163,37 @@ def api_port(service):
return API_PORTS[service]
def additional_install_locations(plugin, source):
'''
Add any required additional package locations for the charm, based
on the Neutron plugin being used. This will also force an immediate
package upgrade.
'''
if plugin == 'Calico':
if config('calico-origin'):
calico_source = config('calico-origin')
else:
release = get_os_codename_install_source(source)
calico_source = 'ppa:project-calico/%s' % release
add_source(calico_source)
apt_update()
apt_upgrade()
def force_etcd_restart():
'''
If etcd has been reconfigured we need to force it to fully restart.
This is necessary because etcd has some config flags that it ignores
after the first time it starts, so we need to make it forget them.
'''
service_stop('etcd')
for directory in glob.glob('/var/lib/etcd/*'):
shutil.rmtree(directory)
service_start('etcd')
def manage_plugin():
return config('manage-neutron-plugin-legacy-mode')

View File

@ -40,6 +40,8 @@ requires:
neutron-plugin-api-subordinate:
interface: neutron-plugin-api-subordinate
scope: container
etcd-proxy:
interface: etcd-proxy
peers:
cluster:
interface: neutron-api-ha

View File

@ -0,0 +1,16 @@
# managed by juju, DO NOT EDIT
description "etcd"
author "etcd maintainers"
start on stopped rc RUNLEVEL=[2345]
stop on runlevel [!2345]
respawn
setuid etcd
env ETCD_DATA_DIR=/var/lib/etcd
export ETCD_DATA_DIR
exec /usr/bin/etcd -proxy on \
-initial-cluster {{ cluster }}

View File

@ -4,6 +4,10 @@
# Configuration file maintained by Juju. Local changes may be overwritten.
###############################################################################
[ml2]
{% if neutron_plugin == 'Calico' -%}
type_drivers = local,flat
mechanism_drivers = calico
{% else -%}
type_drivers = {{ overlay_network_type }},vlan,flat
tenant_network_types = {{ overlay_network_type }},vlan,flat
mechanism_drivers = openvswitch,hyperv,l2population
@ -26,11 +30,16 @@ local_ip = {{ local_ip }}
[agent]
tunnel_types = {{ overlay_network_type }}
{% endif -%}
[securitygroup]
{% if neutron_security_groups -%}
enable_security_group = True
{% if neutron_plugin == 'Calico' -%}
firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
{% else -%}
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
{% endif -%}
{% else -%}
enable_security_group = False
{% endif -%}

View File

@ -30,7 +30,7 @@ core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2'] -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,neutron.services.firewall.fwaas_plugin.FirewallPlugin,neutron.services.loadbalancer.plugin.LoadBalancerPlugin,neutron.services.vpn.plugin.VPNDriverPlugin,neutron.services.metering.metering_plugin.MeteringPlugin
{% endif -%}
{% endif -%}
@ -38,8 +38,16 @@ service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,neu
{% if neutron_security_groups -%}
allow_overlapping_ips = True
{% if neutron_plugin == 'Calico' -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
{% else -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
{% endif -%}
{% endif -%}
{% if neutron_plugin == 'Calico' -%}
dhcp_agents_per_network = 1000
{% endif -%}
{% include "parts/rabbitmq" %}

View File

@ -31,15 +31,23 @@ bind_port = 9696
{% if core_plugin -%}
core_plugin = {{ core_plugin }}
{% if neutron_plugin in ['ovs', 'ml2'] -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,neutron.services.firewall.fwaas_plugin.FirewallPlugin,neutron.services.loadbalancer.plugin.LoadBalancerPlugin,neutron.services.vpn.plugin.VPNDriverPlugin,neutron.services.metering.metering_plugin.MeteringPlugin
{% endif -%}
{% endif -%}
{% if neutron_security_groups -%}
allow_overlapping_ips = True
{% if neutron_plugin == 'Calico' -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
{% else -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
{% endif -%}
{% endif -%}
{% if neutron_plugin == 'Calico' -%}
dhcp_agents_per_network = 1000
{% endif -%}
{% include "parts/rabbitmq" %}

View File

@ -4,6 +4,10 @@
# Configuration file maintained by Juju. Local changes may be overwritten.
###############################################################################
[ml2]
{% if neutron_plugin == 'Calico' -%}
type_drivers = local,flat
mechanism_drivers = calico
{% else -%}
type_drivers = {{ overlay_network_type }},vlan,flat
tenant_network_types = {{ overlay_network_type }},vlan,flat
mechanism_drivers = openvswitch,l2population
@ -26,11 +30,16 @@ local_ip = {{ local_ip }}
[agent]
tunnel_types = {{ overlay_network_type }}
{% endif -%}
[securitygroup]
{% if neutron_security_groups -%}
enable_security_group = True
{% if neutron_plugin == 'Calico' -%}
firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
{% else -%}
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
{% endif -%}
{% else -%}
enable_security_group = False
{% endif -%}

View File

@ -34,7 +34,7 @@ core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2'] -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,lbaas,vpnaas,metering
{% endif -%}
{% endif -%}
@ -42,8 +42,16 @@ service_plugins = router,firewall,lbaas,vpnaas,metering
{% if neutron_security_groups -%}
allow_overlapping_ips = True
{% if neutron_plugin == 'Calico' -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
{% else -%}
neutron_firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
{% endif -%}
{% endif -%}
{% if neutron_plugin == 'Calico' -%}
dhcp_agents_per_network = 1000
{% endif -%}
notify_nova_on_port_status_changes = True
notify_nova_on_port_data_changes = True

View File

@ -438,6 +438,53 @@ class NeutronCCContextTest(CharmTestCase):
self.assertEquals(napi_ctxt[key], expect[key])
class EtcdContextTest(CharmTestCase):
def setUp(self):
super(EtcdContextTest, self).setUp(context, TO_PATCH)
self.relation_get.side_effect = self.test_relation.get
self.config.side_effect = self.test_config.get
self.test_config.set('neutron-plugin', 'Calico')
def tearDown(self):
super(EtcdContextTest, self).tearDown()
def test_etcd_no_related_units(self):
self.related_units.return_value = []
ctxt = context.EtcdContext()()
expect = {'cluster': ''}
self.assertEquals(expect, ctxt)
def test_some_related_units(self):
self.related_units.return_value = ['unit1']
self.relation_ids.return_value = ['rid2', 'rid3']
result = (
'testname=http://172.18.18.18:8888,'
'testname=http://172.18.18.18:8888'
)
self.test_relation.set({'cluster': result})
ctxt = context.EtcdContext()()
expect = {'cluster': result}
self.assertEquals(expect, ctxt)
def test_early_exit(self):
self.test_config.set('neutron-plugin', 'notCalico')
self.related_units.return_value = ['unit1']
self.relation_ids.return_value = ['rid2', 'rid3']
self.test_relation.set({'ip': '172.18.18.18',
'port': 8888,
'name': 'testname'})
ctxt = context.EtcdContext()()
expect = {'cluster': ''}
self.assertEquals(expect, ctxt)
class NeutronApiSDNContextTest(CharmTestCase):
def setUp(self):

View File

@ -62,6 +62,7 @@ TO_PATCH = [
'update_nrpe_config',
'service_reload',
'IdentityServiceContext',
'force_etcd_restart',
]
NEUTRON_CONF_DIR = "/etc/neutron"
@ -440,12 +441,14 @@ class NeutronAPIHooksTests(CharmTestCase):
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
def test_neutron_plugin_api_relation_joined_nol2(self):
self.unit_get.return_value = '172.18.18.18'
self.IdentityServiceContext.return_value = \
DummyContext(return_value={})
_relation_data = {
'neutron-security-groups': False,
'enable-dvr': False,
'enable-l3ha': False,
'addr': '172.18.18.18',
'l2-population': False,
'overlay-network-type': 'vxlan',
'service_protocol': None,
@ -470,12 +473,14 @@ class NeutronAPIHooksTests(CharmTestCase):
)
def test_neutron_plugin_api_relation_joined_dvr(self):
self.unit_get.return_value = '172.18.18.18'
self.IdentityServiceContext.return_value = \
DummyContext(return_value={})
_relation_data = {
'neutron-security-groups': False,
'enable-dvr': True,
'enable-l3ha': False,
'addr': '172.18.18.18',
'l2-population': True,
'overlay-network-type': 'vxlan',
'service_protocol': None,
@ -500,12 +505,14 @@ class NeutronAPIHooksTests(CharmTestCase):
)
def test_neutron_plugin_api_relation_joined_l3ha(self):
self.unit_get.return_value = '172.18.18.18'
self.IdentityServiceContext.return_value = \
DummyContext(return_value={})
_relation_data = {
'neutron-security-groups': False,
'enable-dvr': False,
'enable-l3ha': True,
'addr': '172.18.18.18',
'l2-population': False,
'overlay-network-type': 'vxlan',
'service_protocol': None,
@ -530,11 +537,13 @@ class NeutronAPIHooksTests(CharmTestCase):
)
def test_neutron_plugin_api_relation_joined_w_mtu(self):
self.unit_get.return_value = '172.18.18.18'
self.IdentityServiceContext.return_value = \
DummyContext(return_value={})
self.test_config.set('network-device-mtu', 1500)
_relation_data = {
'neutron-security-groups': False,
'addr': '172.18.18.18',
'l2-population': False,
'overlay-network-type': 'vxlan',
'network-device-mtu': 1500,
@ -750,3 +759,8 @@ class NeutronAPIHooksTests(CharmTestCase):
'Not running neutron database migration as migrations are handled '
'by the neutron-server process or nova-cloud-controller charm.'
)
def test_etcd_peer_joined(self):
self._call_hook('etcd-proxy-relation-joined')
self.assertTrue(self.CONFIGS.register.called)
self.CONFIGS.write.assert_called_with('/etc/init/etcd.conf')

View File

@ -24,6 +24,7 @@ TO_PATCH = [
'apt_install',
'apt_update',
'apt_upgrade',
'add_source',
'b64encode',
'config',
'configure_installation_source',
@ -34,6 +35,9 @@ TO_PATCH = [
'pip_install',
'subprocess',
'is_elected_leader',
'service_stop',
'service_start',
'glob',
]
openstack_origin_git = \
@ -559,3 +563,30 @@ class TestNeutronAPIUtils(CharmTestCase):
self.test_config.set('manage-neutron-plugin-legacy-mode', False)
manage = nutils.manage_plugin()
self.assertFalse(manage)
def test_additional_install_locations_calico(self):
self.get_os_codename_install_source.return_value = 'icehouse'
nutils.additional_install_locations('Calico', '')
self.add_source.assert_called_with('ppa:project-calico/icehouse')
def test_unusual_calico_install_location(self):
self.test_config.set('calico-origin', 'ppa:testppa/project-calico')
nutils.additional_install_locations('Calico', '')
self.add_source.assert_called_with('ppa:testppa/project-calico')
def test_follows_openstack_origin(self):
self.get_os_codename_install_source.return_value = 'juno'
nutils.additional_install_locations('Calico', 'cloud:trusty-juno')
self.add_source.assert_called_with('ppa:project-calico/juno')
@patch('shutil.rmtree')
def test_force_etcd_restart(self, rmtree):
self.glob.glob.return_value = [
'/var/lib/etcd/one', '/var/lib/etcd/two'
]
nutils.force_etcd_restart()
self.service_stop.assert_called_once_with('etcd')
self.glob.glob.assert_called_once_with('/var/lib/etcd/*')
rmtree.assert_any_call('/var/lib/etcd/one')
rmtree.assert_any_call('/var/lib/etcd/two')
self.service_start.assert_called_once_with('etcd')