From 9dfac2fda76a9bd7810d67f24e1b8ed5c16b549d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 10 Apr 2018 16:37:46 +0100 Subject: [PATCH] conf: Add '[neutron] physnets' and related options Things are getting a little bit magic here. We don't want to store inventory in configuration and we _really_ don't want to do so with another JSON blob a la the various '[pci]' options. Placement is not yet at a stage where we can avoid the former but we can certainly avoid the latter. Do so through adding some static opts and some dynamic filter configuration, similarly to what Cinder does for backend configuration using its 'enabled_backends' configuration option. None of this is actually used yet - that will come later. Part of blueprint numa-aware-vswitches Change-Id: Id7c2f0b53c8871ff47a836ec4c324c8cce430b79 --- nova/compute/manager.py | 2 + nova/conf/neutron.py | 56 ++++++++++++++++++++++++++++ nova/tests/unit/conf/__init__.py | 0 nova/tests/unit/conf/test_neutron.py | 33 ++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 nova/tests/unit/conf/__init__.py create mode 100644 nova/tests/unit/conf/test_neutron.py diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 539d59b77ace..a6f3a5904ae2 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1130,6 +1130,8 @@ class ComputeManager(manager.Manager): # if the configuration is wrong. whitelist.Whitelist(CONF.pci.passthrough_whitelist) + nova.conf.neutron.register_dynamic_opts(CONF) + self.driver.init_host(host=self.host) context = nova.context.get_admin_context() instances = objects.InstanceList.get_by_host( diff --git a/nova/conf/neutron.py b/nova/conf/neutron.py index 48cadc77cea9..33fca355dd2b 100644 --- a/nova/conf/neutron.py +++ b/nova/conf/neutron.py @@ -76,6 +76,42 @@ Neutron for extensions. After this number of seconds the next time Nova needs to create a resource in Neutron it will requery Neutron for the extensions that it has loaded. Setting value to 0 will refresh the extensions with no wait. +"""), + cfg.ListOpt('physnets', + default=[], + help=""" +List of physnets present on this host. + +For each *physnet* listed, an additional section, +``[neutron_physnet_$PHYSNET]``, will be added to the configuration file. Each +section must be configured with a single configuration option, ``numa_nodes``, +which should be a list of node IDs for all NUMA nodes this physnet is +associated with. For example:: + + [neutron] + physnets = foo, bar + + [neutron_physnet_foo] + numa_nodes = 0 + + [neutron_physnet_bar] + numa_nodes = 0,1 + +Any *physnet* that is not listed using this option will be treated as having no +particular NUMA node affinity. + +Tunnelled networks (VXLAN, GRE, ...) cannot be accounted for in this way and +are instead configured using the ``[neutron_tunnel]`` group. For example:: + + [neutron_tunnel] + numa_nodes = 1 + +Related options: + +* ``[neutron_tunnel] numa_nodes`` can be used to configure NUMA affinity for + all tunneled networks +* ``[neutron_physnet_$PHYSNET] numa_nodes`` must be configured for each value + of ``$PHYSNET`` specified by this option """), ] @@ -118,6 +154,26 @@ def register_opts(conf): confutils.register_ksa_opts(conf, neutron_group, DEFAULT_SERVICE_TYPE) +def register_dynamic_opts(conf): + """Register dynamically-generated options and groups. + + This must be called by the service that wishes to use the options **after** + the initial configuration has been loaded. + """ + opt = cfg.ListOpt('numa_nodes', default=[], item_type=cfg.types.Integer()) + + # Register the '[neutron_tunnel] numa_nodes' opt, implicitly + # registering the '[neutron_tunnel]' group in the process. This could + # be done statically but is done to avoid this group appearing in + # nova.conf documentation while the other group does not. + conf.register_opt(opt, group='neutron_tunnel') + + # Register the '[neutron_physnet_$PHYSNET] numa_nodes' opts, implicitly + # registering the '[neutron_physnet_$PHYSNET]' groups in the process + for physnet in conf.neutron.physnets: + conf.register_opt(opt, group='neutron_physnet_%s' % physnet) + + def list_opts(): return { neutron_group: ( diff --git a/nova/tests/unit/conf/__init__.py b/nova/tests/unit/conf/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/nova/tests/unit/conf/test_neutron.py b/nova/tests/unit/conf/test_neutron.py new file mode 100644 index 000000000000..b784486f9d46 --- /dev/null +++ b/nova/tests/unit/conf/test_neutron.py @@ -0,0 +1,33 @@ +# Copyright 2018 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import nova.conf +from nova import test + + +CONF = nova.conf.CONF + + +class NeutronConfTestCase(test.NoDBTestCase): + + def test_register_dynamic_opts(self): + self.flags(physnets=['foo', 'bar', 'baz'], group='neutron') + + self.assertNotIn('neutron_physnet_foo', CONF) + self.assertNotIn('neutron_physnet_bar', CONF) + + nova.conf.neutron.register_dynamic_opts(CONF) + + self.assertIn('neutron_physnet_foo', CONF) + self.assertIn('neutron_physnet_bar', CONF)