diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index 15f9fd5699..009ef5189a 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -1081,6 +1081,7 @@ designate_notifications_topic_name: "notifications_designate" ####################### neutron_bgp_router_id: "1.1.1.1" neutron_bridge_name: "{{ 'br-dvs' if neutron_plugin_agent == 'vmware_dvs' else 'br_dpdk' if enable_ovs_dpdk | bool else 'br-ex' }}" +neutron_physical_networks: "{% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index }}{% if not loop.last %},{% endif %}{% endfor %}" # Comma-separated type of enabled ml2 type drivers neutron_type_drivers: "flat,vlan,vxlan{% if neutron_plugin_agent == 'ovn' %},geneve{% endif %}" # Comma-separated types of tenant networks (should be listed in 'neutron_type_drivers') diff --git a/ansible/roles/neutron/templates/linuxbridge_agent.ini.j2 b/ansible/roles/neutron/templates/linuxbridge_agent.ini.j2 index 1dbaae0ede..5b0ae990b8 100644 --- a/ansible/roles/neutron/templates/linuxbridge_agent.ini.j2 +++ b/ansible/roles/neutron/templates/linuxbridge_agent.ini.j2 @@ -5,7 +5,8 @@ extensions = {{ neutron_agent_extensions|map(attribute='name')|join(',') }} [linux_bridge] {% if inventory_hostname in groups["network"] or (inventory_hostname in groups["compute"] and computes_need_external_bridge | bool ) %} -physical_interface_mappings = {% for interface in neutron_external_interface.split(',') %}physnet{{ loop.index0 + 1 }}:{{ interface }}{% if not loop.last %},{% endif %}{% endfor %} +{# Format: physnet1:br1,physnet2:br2 #} +physical_interface_mappings = {{ neutron_physical_networks.split(',') | zip(neutron_external_interface.split(',')) | map('join', ':') | join(',') }} {% endif %} [securitygroup] diff --git a/ansible/roles/neutron/templates/ml2_conf.ini.j2 b/ansible/roles/neutron/templates/ml2_conf.ini.j2 index 9c70eb898a..0e34477691 100644 --- a/ansible/roles/neutron/templates/ml2_conf.ini.j2 +++ b/ansible/roles/neutron/templates/ml2_conf.ini.j2 @@ -15,7 +15,7 @@ extension_drivers = {{ neutron_extension_drivers | map(attribute='name') | join( [ml2_type_vlan] {% if enable_ironic | bool %} -network_vlan_ranges = physnet1 +network_vlan_ranges = {{ neutron_physical_networks }} {% else %} network_vlan_ranges = {% endif %} @@ -24,7 +24,7 @@ network_vlan_ranges = {% if enable_ironic | bool %} flat_networks = * {% else %} -flat_networks = {% for interface in neutron_external_interface.split(',') %}physnet{{ loop.index0 + 1 }}{% if not loop.last %},{% endif %}{% endfor %} +flat_networks = {{ neutron_physical_networks }} {% endif %} [ml2_type_vxlan] diff --git a/ansible/roles/neutron/templates/openvswitch_agent.ini.j2 b/ansible/roles/neutron/templates/openvswitch_agent.ini.j2 index 88834e2dea..8ac25af7e1 100644 --- a/ansible/roles/neutron/templates/openvswitch_agent.ini.j2 +++ b/ansible/roles/neutron/templates/openvswitch_agent.ini.j2 @@ -15,7 +15,8 @@ firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewal [ovs] {% if inventory_hostname in groups["network"] or (inventory_hostname in groups["compute"] and computes_need_external_bridge | bool ) %} -bridge_mappings = {% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index0 + 1 }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %} +{# Format: physnet1:br1,physnet2:br2 #} +bridge_mappings = {{ neutron_physical_networks.split(',') | zip(neutron_bridge_name.split(',')) | map('join', ':') | join(',') }} {% endif %} datapath_type = {{ ovs_datapath }} ovsdb_connection = tcp:127.0.0.1:{{ ovsdb_port }} diff --git a/ansible/roles/ovn-controller/tasks/setup-ovs.yml b/ansible/roles/ovn-controller/tasks/setup-ovs.yml index 5ac61a16b3..0d9ab0e30c 100644 --- a/ansible/roles/ovn-controller/tasks/setup-ovs.yml +++ b/ansible/roles/ovn-controller/tasks/setup-ovs.yml @@ -12,8 +12,10 @@ - name: Configure OVN in OVSDB vars: - ovn_mappings: "{% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index0 + 1 }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %}" - ovn_macs: "{% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index0 + 1 }}:{{ ovn_base_mac | random_mac(seed=inventory_hostname + bridge) }}{% if not loop.last %},{% endif %}{% endfor %}" + # Format: physnet1:br1,physnet2:br2 + ovn_mappings: "{{ neutron_physical_networks.split(',') | zip(neutron_bridge_name.split(',')) | map('join', ':') | join(',') }}" + # Format: physnet1:00:11:22:33:44:55,physnet2:00:11:22:33:44:56 + ovn_macs: "{% for physnet, bridge in neutron_physical_networks.split(',') | zip(neutron_bridge_name.split(',')) %}{{ physnet }}:{{ ovn_base_mac | random_mac(seed=inventory_hostname + bridge) }}{% if not loop.last %},{% endif %}{% endfor %}" ovn_cms_opts: "{{ 'enable-chassis-as-gw' if inventory_hostname in groups['ovn-controller-network'] else '' }}{{ ',availability-zones=' + neutron_ovn_availability_zones | join(',') if inventory_hostname in groups['ovn-controller-network'] and neutron_ovn_availability_zones }}" become: true kolla_toolbox: diff --git a/ansible/roles/ovs-dpdk/defaults/main.yml b/ansible/roles/ovs-dpdk/defaults/main.yml index 5a717a3adc..b85b058c87 100644 --- a/ansible/roles/ovs-dpdk/defaults/main.yml +++ b/ansible/roles/ovs-dpdk/defaults/main.yml @@ -37,8 +37,10 @@ ovsdpdk_services: #################### # OVS #################### -ovs_bridge_mappings: "{% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index0 + 1 }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %}" -ovs_port_mappings: "{% for bridge in neutron_bridge_name.split(',') %} {{ neutron_external_interface.split(',')[loop.index0] }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %}" +# Format: physnet1:br1,physnet2:br2 +ovs_bridge_mappings: "{{ neutron_physical_networks.split(',') | zip(neutron_bridge_name.split(',')) | map('join', ':') | join(',') }}" +# Format: eth1:br1,eth2:br2 +ovs_port_mappings: "{{ neutron_external_interface.split(',') | zip(neutron_bridge_name.split(',')) | map('join', ':') | join(',') }}" tunnel_interface_network: "{{ hostvars[inventory_hostname].ansible_facts[dpdk_tunnel_interface]['ipv4']['network'] }}/{{ hostvars[inventory_hostname].ansible_facts[dpdk_tunnel_interface]['ipv4']['netmask'] }}" tunnel_interface_cidr: "{{ dpdk_tunnel_interface_address }}/{{ tunnel_interface_network | ipaddr('prefix') }}" ovs_cidr_mappings: "{% if neutron_bridge_name.split(',') | length != 1 %} {neutron_bridge_name.split(',')[0]}:{{ tunnel_interface_cidr }} {% else %} {{ neutron_bridge_name }}:{{ tunnel_interface_cidr }} {% endif %}" diff --git a/doc/source/reference/networking/neutron.rst b/doc/source/reference/networking/neutron.rst index c748c6d4e4..8a627572d7 100644 --- a/doc/source/reference/networking/neutron.rst +++ b/doc/source/reference/networking/neutron.rst @@ -20,13 +20,20 @@ Neutron external interface is used for communication with the external world, for example provider networks, routers and floating IPs. For setting up the neutron external interface modify ``/etc/kolla/globals.yml`` setting ``neutron_external_interface`` to the -desired interface name. This interface is used by hosts in the ``network`` -group. It is also used by hosts in the ``compute`` group if +desired interface name or comma-separated list of interface names. Its default +value is ``eth1``. These external interfaces are used by hosts in the +``network`` group. They are also used by hosts in the ``compute`` group if ``enable_neutron_provider_networks`` is set or DVR is enabled. -The interface is plugged into a bridge (Open vSwitch or Linux Bridge, depending -on the driver) defined by ``neutron_bridge_name``, which defaults to ``br-ex``. -The default Neutron physical network is ``physnet1``. +The external interfaces are each plugged into a bridge (Open vSwitch or Linux +Bridge, depending on the driver) defined by ``neutron_bridge_name``, which +defaults to ``br-ex``. When there are multiple external interfaces, +``neutron_bridge_name`` should be a comma-separated list of the same length. + +The default Neutron physical network is ``physnet1``, or ``physnet1`` to +``physnetN`` when there are multiple external network interfaces. This may be +changed by setting ``neutron_physical_networks`` to a comma-separated list of +networks of the same length. Example: single interface ------------------------- @@ -54,6 +61,30 @@ These two lists are "zipped" together, such that ``eth1`` is plugged into the Ansible maps these interfaces to Neutron physical networks ``physnet1`` and ``physnet2`` respectively. +Example: custom physical networks +--------------------------------- + +Sometimes we may want to customise the physical network names used. This may be +to allow for not all hosts having access to all physical networks, or to use +more descriptive names. + +For example, in an environment with a separate physical network for Ironic +provisioning, controllers might have access to two physical networks: + +.. code-block:: yaml + + neutron_external_interface: "eth1,eth2" + neutron_bridge_name: "br-ex1,br-ex2" + neutron_physical_network: "physnet1,physnet2" + +While compute nodes have access only to ``physnet2``. + +.. code-block:: yaml + + neutron_external_interface: "eth1" + neutron_bridge_name: "br-ex1" + neutron_physical_network: "physnet2" + Example: shared interface ------------------------- diff --git a/releasenotes/notes/neutron-physical-networks-5b908bed9809c3b4.yaml b/releasenotes/notes/neutron-physical-networks-5b908bed9809c3b4.yaml new file mode 100644 index 0000000000..a62e400089 --- /dev/null +++ b/releasenotes/notes/neutron-physical-networks-5b908bed9809c3b4.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds a ``neutron_physical_networks`` variable for customising Neutron + physical network names. The default behaviour of using ``physnet1`` to + ``physnetN`` is unchanged.