Add QoS support

This patch adds the enable-qos option to the charm. If enable-qos is
set then neutron.services.qos.qos_plugin.QoSPlugin is added to
service_plugins in neutron.conf locally. The
neutron-plugin-api-relation has also been updated to send the
enable-qos option to charms connected over that relation (for
example neutron-openvswitch and neutron-gateway).

As part of this some of the logic for setting service_plugins was
removed from the neutron.conf and placed in the NeutronCCContext.

This patch is based on the steps in:
https://docs.openstack.org/mitaka/networking-guide/config-qos.html

Change-Id: I1beba9bebdb7766fd95d47bf13b6f4ad86e762b5
Partial-Bug: #1705358
This commit is contained in:
Liam Young 2017-08-07 17:05:28 +01:00 committed by Liam Young
parent ea08e0af12
commit a8e6824e3a
14 changed files with 221 additions and 28 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ tags
.unit-state.db
trusty/
xenial/
.stestr

View File

@ -381,6 +381,11 @@ options:
Seconds between nodes reporting state to server; should be less than agent_down_time,
best if it is half or less than agent_down_time.
Used by all neutron agents (includes l2 agents and other types of agents).
enable-qos:
type: boolean
default: False
description: |
Enable QoS (assuming the plug-in or ml2 mechanism driver can support it)
# Quota config
quota-security-group:
type: int

View File

@ -47,6 +47,7 @@ TENANT_NET_TYPES = [VXLAN, GRE, VLAN, FLAT, LOCAL]
EXTENSION_DRIVER_PORT_SECURITY = 'port_security'
EXTENSION_DRIVER_DNS = 'dns'
EXTENSION_DRIVER_QOS = 'qos'
ETC_NEUTRON = '/etc/neutron'
@ -201,6 +202,23 @@ def get_ml2_mechanism_drivers():
return ','.join(mechanism_drivers)
def is_qos_requested_and_valid():
"""Check whether QoS should be enabled by checking whether it has been
requested and, if it has, is it supported in the current configuration
"""
if config('enable-qos'):
if CompareOpenStackReleases(os_release('neutron-server')) < 'mitaka':
msg = ("The enable-qos option is only supported on mitaka or "
"later")
log(msg, ERROR)
return False
else:
return True
else:
return False
class ApacheSSLContext(context.ApacheSSLContext):
interfaces = ['https']
@ -391,6 +409,10 @@ class NeutronCCContext(context.NeutronContext):
if dns_domain:
extension_drivers.append(EXTENSION_DRIVER_DNS)
ctxt['dns_domain'] = dns_domain
if is_qos_requested_and_valid():
extension_drivers.append(EXTENSION_DRIVER_QOS)
if extension_drivers:
ctxt['extension_drivers'] = ','.join(extension_drivers)
@ -412,6 +434,47 @@ class NeutronCCContext(context.NeutronContext):
ctxt['mechanism_drivers'] = get_ml2_mechanism_drivers()
if config('neutron-plugin') in ['ovs', 'ml2', 'Calico']:
ctxt['service_plugins'] = []
service_plugins = {
'icehouse': [
('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')],
'juno': [
('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')],
'kilo': ['router', 'firewall', 'lbaas', 'vpnaas', 'metering'],
'liberty': ['router', 'firewall', 'lbaas', 'vpnaas',
'metering'],
'mitaka': ['router', 'firewall', 'lbaas', 'vpnaas',
'metering'],
'newton': ['router', 'firewall', 'vpnaas', 'metering',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2')],
'ocata': ['router', 'firewall', 'vpnaas', 'metering',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2')],
'pike': ['router', 'firewall', 'metering',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2')],
}
ctxt['service_plugins'] = service_plugins.get(
release, service_plugins['pike'])
if is_qos_requested_and_valid():
ctxt['service_plugins'].append('qos')
ctxt['service_plugins'] = ','.join(ctxt['service_plugins'])
return ctxt

View File

@ -94,6 +94,7 @@ from neutron_api_context import (
get_l2population,
get_overlay_network_type,
IdentityServiceContext,
is_qos_requested_and_valid,
EtcdContext,
)
@ -484,6 +485,7 @@ def neutron_plugin_api_relation_joined(rid=None):
'l2-population': get_l2population(),
'enable-dvr': get_dvr(),
'enable-l3ha': get_l3ha(),
'enable-qos': is_qos_requested_and_valid(),
'overlay-network-type': get_overlay_network_type(),
'addr': unit_get('private-address'),
'polling-interval': config('polling-interval'),

View File

@ -29,10 +29,6 @@ bind_port = 9696
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% 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 -%}
{% endif -%}

View File

@ -31,8 +31,8 @@ bind_port = 9696
{% if core_plugin -%}
core_plugin = {{ core_plugin }}
{% 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
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% endif -%}
{% endif -%}

View File

@ -33,10 +33,6 @@ bind_port = 9696
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,lbaas,vpnaas,metering
{% endif -%}
{% endif -%}
{% endif -%}

View File

@ -38,14 +38,10 @@ api_extensions_path = {{ api_extensions_path }}
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,lbaas,vpnaas,metering
{% elif neutron_plugin == 'midonet' -%}
service_plugins = lbaas
{% endif -%}
{% endif -%}
{% endif -%}
{% if neutron_security_groups -%}
allow_overlapping_ips = True

View File

@ -36,10 +36,6 @@ bind_port = 9696
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,lbaas,vpnaas,metering
{% endif -%}
{% endif -%}
{% endif -%}

View File

@ -36,10 +36,6 @@ bind_port = 9696
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,vpnaas,metering,neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2
{% endif -%}
{% endif -%}
{% endif -%}

View File

@ -32,10 +32,6 @@ bind_port = 9696
core_plugin = {{ core_plugin }}
{% if service_plugins -%}
service_plugins = {{ service_plugins }}
{% else -%}
{% if neutron_plugin in ['ovs', 'ml2', 'Calico'] -%}
service_plugins = router,firewall,metering,neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2
{% endif -%}
{% endif -%}
{% endif -%}

View File

@ -660,6 +660,39 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
message = "ml2 config error: {}".format(ret)
amulet.raise_status(amulet.FAIL, msg=message)
def test_400_enable_qos(self):
"""Check qos settings"""
if self._get_openstack_release() >= self.trusty_mitaka:
u.log.debug('Checking QoS setting in neutron-api '
'neutron-openvswitch relation data...')
unit = self.neutron_api_sentry
relation = ['neutron-plugin-api',
'neutron-openvswitch:neutron-plugin-api']
set_default = {'enable-qos': 'False'}
set_alternate = {'enable-qos': 'True'}
self.d.configure('neutron-api', set_alternate)
relation_data = unit.relation(relation[0], relation[1])
if relation_data['enable-qos'] != 'True':
message = ("enable-qos setting not set properly on "
"neutron-plugin-api relation")
amulet.raise_status(amulet.FAIL, msg=message)
qos_plugin = 'qos'
config = u._get_config(unit, '/etc/neutron/neutron.conf')
service_plugins = config.get(
'DEFAULT',
'service_plugins').split(',')
if qos_plugin not in service_plugins:
message = "{} not in service_plugins".format(qos_plugin)
amulet.raise_status(amulet.FAIL, msg=message)
u.log.debug('Setting QoS back to {}'.format(
set_default['enable-qos']))
self.d.configure('neutron-api', set_default)
u.log.debug('OK')
def test_900_restart_on_config_change(self):
"""Verify that the specified services are restarted when the
config is changed."""

View File

@ -442,9 +442,15 @@ class NeutronCCContextTest(CharmTestCase):
'vlan_ranges': 'physnet1:1000:2000',
'vni_ranges': '1001:2000',
'extension_drivers': 'port_security',
'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'),
}
napi_ctxt = context.NeutronCCContext()
self.os_release.return_value = 'havana'
self.os_release.return_value = 'icehouse'
with patch.object(napi_ctxt, '_ensure_packages'):
self.assertEquals(ctxt_data, napi_ctxt())
@ -528,9 +534,15 @@ class NeutronCCContextTest(CharmTestCase):
'vni_ranges': '1001:2000,3001:4000',
'network_providers': 'physnet2,physnet3',
'extension_drivers': 'port_security',
'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'),
}
napi_ctxt = context.NeutronCCContext()
self.os_release.return_value = 'havana'
self.os_release.return_value = 'icehouse'
with patch.object(napi_ctxt, '_ensure_packages'):
self.assertEquals(ctxt_data, napi_ctxt())
@ -540,10 +552,12 @@ class NeutronCCContextTest(CharmTestCase):
def test_neutroncc_context_l3ha(self, _import, plugin, nm):
plugin.return_value = None
self.test_config.set('enable-l3ha', True)
self.test_config.set('enable-qos', False)
self.test_config.set('overlay-network-type', 'gre')
self.test_config.set('neutron-plugin', 'ovs')
self.test_config.set('l2-population', False)
self.os_release.return_value = 'juno'
self.maxDiff = None
ctxt_data = {
'debug': True,
'enable_dvr': False,
@ -573,6 +587,12 @@ class NeutronCCContextTest(CharmTestCase):
'vlan_ranges': 'physnet1:1000:2000',
'vni_ranges': '1001:2000',
'extension_drivers': 'port_security',
'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'),
}
napi_ctxt = context.NeutronCCContext()
with patch.object(napi_ctxt, '_ensure_packages'):
@ -629,6 +649,7 @@ class NeutronCCContextTest(CharmTestCase):
'vlan_ranges': 'physnet1:1000:2000',
'vni_ranges': '1001:2000',
'extension_drivers': 'port_security',
'service_plugins': 'router,firewall,lbaas,vpnaas,metering',
}
napi_ctxt = context.NeutronCCContext()
self.os_release.return_value = 'kilo'
@ -712,6 +733,87 @@ class NeutronCCContextTest(CharmTestCase):
for key in expect.iterkeys():
self.assertEquals(napi_ctxt[key], expect[key])
@patch.object(context.NeutronCCContext, 'network_manager')
@patch.object(context.NeutronCCContext, 'plugin')
@patch('__builtin__.__import__')
def test_neutroncc_context_qos(self, _import, plugin, nm):
plugin.return_value = None
self.os_release.return_value = 'mitaka'
self.test_config.set('enable-qos', True)
self.test_config.set('enable-ml2-port-security', False)
napi_ctxt = context.NeutronCCContext()()
service_plugins = ('router,firewall,lbaas,vpnaas,metering,qos')
expect = {
'extension_drivers': 'qos',
'service_plugins': service_plugins,
}
for key in expect.iterkeys():
self.assertEqual(napi_ctxt[key], expect[key])
@patch.object(context.NeutronCCContext, 'network_manager')
@patch.object(context.NeutronCCContext, 'plugin')
@patch('__builtin__.__import__')
def test_neutroncc_context_service_plugins(self, _import, plugin, nm):
plugin.return_value = None
self.test_config.set('enable-qos', False)
self.test_config.set('enable-ml2-port-security', False)
# icehouse
self.os_release.return_value = 'icehouse'
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')
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# juno
self.os_release.return_value = 'juno'
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')
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# kilo
self.os_release.return_value = 'kilo'
service_plugins = 'router,firewall,lbaas,vpnaas,metering'
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# liberty
self.os_release.return_value = 'liberty'
service_plugins = 'router,firewall,lbaas,vpnaas,metering'
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# mitaka
self.os_release.return_value = 'mitaka'
service_plugins = 'router,firewall,lbaas,vpnaas,metering'
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# newton
self.os_release.return_value = 'newton'
service_plugins = (
'router,firewall,vpnaas,metering,'
'neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2')
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# ocata
self.os_release.return_value = 'ocata'
service_plugins = (
'router,firewall,vpnaas,metering,'
'neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2')
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
# pike
self.os_release.return_value = 'pike'
service_plugins = (
'router,firewall,metering,'
'neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2')
self.assertEqual(context.NeutronCCContext()()['service_plugins'],
service_plugins)
class EtcdContextTest(CharmTestCase):

View File

@ -67,6 +67,7 @@ TO_PATCH = [
'get_overlay_network_type',
'git_install',
'is_elected_leader',
'is_qos_requested_and_valid',
'is_relation_made',
'log',
'migrate_neutron_database',
@ -602,6 +603,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'neutron-security-groups': False,
'enable-dvr': False,
'enable-l3ha': False,
'enable-qos': False,
'addr': '172.18.18.18',
'polling-interval': 2,
'rpc-response-timeout': 60,
@ -620,6 +622,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'service_host': None,
'neutron-api-ready': 'no',
}
self.is_qos_requested_and_valid.return_value = False
self.get_dvr.return_value = False
self.get_l3ha.return_value = False
self.get_l2population.return_value = False
@ -639,6 +642,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'neutron-security-groups': False,
'enable-dvr': True,
'enable-l3ha': False,
'enable-qos': False,
'addr': '172.18.18.18',
'polling-interval': 2,
'rpc-response-timeout': 60,
@ -657,6 +661,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'service_host': None,
'neutron-api-ready': 'no',
}
self.is_qos_requested_and_valid.return_value = False
self.get_dvr.return_value = True
self.get_l3ha.return_value = False
self.get_l2population.return_value = True
@ -676,6 +681,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'neutron-security-groups': False,
'enable-dvr': False,
'enable-l3ha': True,
'enable-qos': False,
'addr': '172.18.18.18',
'polling-interval': 2,
'rpc-response-timeout': 60,
@ -694,6 +700,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'service_host': None,
'neutron-api-ready': 'no',
}
self.is_qos_requested_and_valid.return_value = False
self.get_dvr.return_value = False
self.get_l3ha.return_value = True
self.get_l2population.return_value = False
@ -717,6 +724,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'rpc-response-timeout': 60,
'report-interval': 30,
'l2-population': False,
'enable-qos': False,
'overlay-network-type': 'vxlan',
'network-device-mtu': 1500,
'enable-l3ha': True,
@ -733,6 +741,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'service_host': None,
'neutron-api-ready': 'no',
}
self.is_qos_requested_and_valid.return_value = False
self.get_dvr.return_value = True
self.get_l3ha.return_value = True
self.get_l2population.return_value = False
@ -752,6 +761,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'neutron-security-groups': False,
'enable-dvr': False,
'enable-l3ha': False,
'enable-qos': False,
'addr': '172.18.18.18',
'polling-interval': 2,
'rpc-response-timeout': 60,
@ -771,6 +781,7 @@ class NeutronAPIHooksTests(CharmTestCase):
'neutron-api-ready': 'no',
'dns-domain': 'openstack.example.'
}
self.is_qos_requested_and_valid.return_value = False
self.get_dvr.return_value = False
self.get_l3ha.return_value = False
self.get_l2population.return_value = False