In the old days (so, like, last year), Neutron supported a single external network per L3 agent. You would run something like this…

$ neutron net-create external --router:external=true

…and neutron would map this to the bridge defined in external_network_bridge in /etc/neutron/l3_agent.ini. If you wanted to support more than a single external network, you would need to run multiple L3 agents, each with a unique value for external_network_bridge.

There is now a better option available.

Assumptions

In this post, I’m assuming:

  • You’re using the ML2 plugin for Neutron.
  • You’re using the Open vSwitch mechanism driver for the ML2 plugin
  • You have eth1 and eth2 connected directly to networks that you would like to make available as external networks in OpenStack.

Create your bridges

For each external network you wish to support, create a new OVS bridge. For example, assuming that we want to make a network attached to eth1 and a network attached to eth2 available to tenants:

# ovs-vsctl add-br br-eth1
# ovs-vsctl add-port br-eth1 eth1
# ovs-vsctl add-br br-eth2
# ovs-vsctl add-port br-eth2 eth2

Realistically, you would accomplish this via your system’s native network configuration mechanism, but I’m going to gloss over that detail for now.

Configure the L3 Agent

Start with the following comment in l3_agent.ini:

# When external_network_bridge is set, each L3 agent can be associated
# with no more than one external network. This value should be set to the UUID
# of that external network. To allow L3 agent support multiple external
# networks, both the external_network_bridge and gateway_external_network_id
# must be left empty.

Following those instructions, make sure that both external_network_bridge and gateway_external_network_id are unset in l3_agent.ini.

Configure the ML2 Plugin

We are creating “flat” networks in this example, so we need to make sure that we can create flat networks. Make sure that the type_drivers parameter of the [ml2] section of your plugin configuration includes flat:

[ml2]
type_drivers = local,flat,gre,vxlan

In the [ml2_type_flat] section, need to create a list of physical network names that can be used to create flat networks. If you want all physical networks to be available for flat networks, you can use *:

[ml2_type_flat]
flat_networks = *    

Both of these changes probably go in /etc/neutron/plugin.ini, but may going elsewhere depending on how your system is configured.

Configure the Open vSwitch Agent

For each bridge, you will need to add entries to both the network_vlan_ranges and bridge_mappings parameters of the [ovs] section of your plugin configuration. For the purposes of this post, that means:

[ovs]
network_vlan_ranges = physnet1,physnet2
bridge_mappings = physnet1:br-eth1,physnet2:br-eth2

This will probably go in /etc/neutron/plugin.ini. Specifically, it needs to go wherever your neutron-openvswitch-agent process is looking for configuration information. So you if you see this:

$ ps -fe | grep openvswitch-agent
neutron  12529     1  0 09:50 ?        00:00:08 /usr/bin/python /usr/bin/neutron-openvswitch-agent --config-file /usr/share/neutron/neutron-dist.conf --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini --log-file /var/log/neutron/openvswitch-agent.log

…then you would make the changes to /etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini.

Restart Neutron

You will need to restart both the l3 agent and the openvswitch agent. If you’re on a recent Fedora/RHEL/CentOS, you can restart all Neutron services like this:

# openstack-service restart neutron

Inspect your Open vSwitch Configuration

As root, run ovs-vsctl show. You should see something like this:

f4a4312b-307e-4c3c-b728-9434000a34ff
    Bridge br-int
        Port br-int
            Interface br-int
                type: internal
        Port "int-br-eth2"
            Interface "int-br-eth2"
        Port int-br-ex
            Interface int-br-ex
        Port "int-br-eth1"
            Interface "int-br-eth1"
    Bridge "br-eth2"
        Port "br-eth2"
            Interface "br-eth2"
                type: internal
        Port "phy-br-eth2"
            Interface "phy-br-eth2"
        Port "eth2"
            Interface "eth2"
    Bridge "br-eth1"
        Port "br-eth1"
            Interface "br-eth1"
                type: internal
        Port "phy-br-eth1"
            Interface "phy-br-eth1"
        Port "eth1"
            Interface "eth1"
    ovs_version: "2.0.1"

Here you can see the OVS bridge br-eth1 and br-eth2, each with the appropriate associated physical interface and links to the integration bridge, br-int.

Create your external networks

With admin credentials, use the net-create and subnet-create commands to create the appropiate networks:

$ neutron net-create external1 -- --router:external=true \
  --provider:network_type=flat \
  --provider:physical_network=physnet1
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| id                        | 23f4b5f6-14fd-4bab-a8b0-445257bbc0d1 |
| name                      | external1                            |
| provider:network_type     | flat                                 |
| provider:physical_network | physnet1                             |
| provider:segmentation_id  |                                      |
| router:external           | True                                 |
| shared                    | False                                |
| status                    | ACTIVE                               |
| subnets                   |                                      |
| tenant_id                 | 6f736b1361b74789a81d4d53d88be3c5     |
+---------------------------+--------------------------------------+
$ neutron subnet-create --disable-dhcp external1 10.1.0.0/24
+------------------+--------------------------------------------+
| Field            | Value                                      |
+------------------+--------------------------------------------+
| allocation_pools | {"start": "10.1.0.2", "end": "10.1.0.254"} |
| cidr             | 10.1.0.0/24                                |
| dns_nameservers  |                                            |
| enable_dhcp      | False                                      |
| gateway_ip       | 10.1.0.1                                   |
| host_routes      |                                            |
| id               | 363ba289-a989-4acb-ac3b-ffaeb90796fc       |
| ip_version       | 4                                          |
| name             |                                            |
| network_id       | 23f4b5f6-14fd-4bab-a8b0-445257bbc0d1       |
| tenant_id        | 6f736b1361b74789a81d4d53d88be3c5           |
+------------------+--------------------------------------------+


$ neutron net-create external2 -- --router:external=true \
  --provider:network_type=flat \
  --provider:physical_network=physnet2
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| id                        | 762be5de-31a2-46b8-925c-0967871f8181 |
| name                      | external2                            |
| provider:network_type     | flat                                 |
| provider:physical_network | physnet2                             |
| provider:segmentation_id  |                                      |
| router:external           | True                                 |
| shared                    | False                                |
| status                    | ACTIVE                               |
| subnets                   |                                      |
| tenant_id                 | 6f736b1361b74789a81d4d53d88be3c5     |
+---------------------------+--------------------------------------+
$ neutron subnet-create --disable-dhcp external2 10.2.0.0/24
+------------------+--------------------------------------------+
| Field            | Value                                      |
+------------------+--------------------------------------------+
| allocation_pools | {"start": "10.2.0.2", "end": "10.2.0.254"} |
| cidr             | 10.2.0.0/24                                |
| dns_nameservers  |                                            |
| enable_dhcp      | False                                      |
| gateway_ip       | 10.2.0.1                                   |
| host_routes      |                                            |
| id               | edffc5c6-0e16-4da0-8eba-9d79ab9fd2fe       |
| ip_version       | 4                                          |
| name             |                                            |
| network_id       | 762be5de-31a2-46b8-925c-0967871f8181       |
| tenant_id        | 6f736b1361b74789a81d4d53d88be3c5           |
+------------------+--------------------------------------------+

This assumes that eth1 is connected to a network using 10.1.0.0/24 and eth2 is connected to a network using 10.2.0.0/24, and that each network has a gateway sitting at the corresponding .1 address.

And you’re all set!