This article is intended as simple tutorial on how to add a configuration option to the OpenContrail control-node that controls the behavior of the BGP implementation.
The configuration node (the schema-transformer process) automatically assigns route-targets to routing-instances that are created in order to implement network virtualization. These route-targets are assigned from the space corresponding to the autonomous-system that the data-center cluster is on (64512 by default).
The user can additionally define an external route target for networks that extend beyond the data-center boundary (e.g. the public network). For this tutorial we will implement a knob that strips out route target communities that contain private ASes.
In order to get started lets create a sandbox and initialize it. In a unix shell type the following commands:
The first step towards defining a new knob is to add it to the schema. OpenContrail auto-generates the REST API that stores the configuration and makes it available through the IF-MAP server. It also generates the API client library that is can be used to set the configuration parameters. The BGP related schema is present in controller/src/schema/bgp_schema.xsd.
Use your favorite text editor to modify this file and add a new XSD element called “rtarget-strip-private” in the type BgpSessionAttributes. This is the data type that is associated with bgp peering sessions.
After editing this file, execute the command scons controller/src/api-lib. This command builds the Python client api library that we will use later on to set the new configuration parameter. You can poke around at the generated code:
grep rtarget-strip-private build/debug/api-lib/vnc_api/gen/*
Should yield a couple of references to the newly added configuration parameter.
In order to use this new parameter we will modify the helper script typically used to configure BGP peering sessions with an external BGP speaker (controller/src/config/utils/provision_bgp.py). As an example, we will add the new attribute only when configuring BGP sessions of for a ‘router_type’ of “mx”.
At this point, we need to generate a test api-server in order to validate our code. The following sequence of shell commands will build and start a mock api-server on port 50000.
The next step is to create a test script that will configure 2 BGP peers and a session between them. This can be achieved by creating a new file (controller/src/config/api-server/tests/test_bgp_session.py) that executes our test case.
After executing this script, we should be able to see the contents of the “bgp-routers” table using the command:
wget -O- http://localhost:50000/bgp-routers | python -mjson.tool
This should display 2 entries, each with the router name specified in our test script. Examine the “cn-test” router by using the “wget” command with the “href” of the “cn-test” router. It will display the new knob (rtarget-strip-private).
Implementing the BGP component
Before proceeding with the implementation we need to look into how BGP update generation works. In the OpenContrail implementation, the routing table is responsible for calculating the desired advertisement attributes (the RibOut attributes). The RibOut class has a dual purpose: it holds the head of the update queue; it also groups the peers that advertise this table with the same export policy, represented by the RibExportPolicy class.
Additional background information on the design of the BGP component can be found here.
Since our new knob modifies the BGP attributes that are generated, the first thing that we need to do is to add a new member to the RibExportPolicy class (rtarget_strip_private) and modify the constructor to include the new parameter.
By modifying the constructor we can discover that the “policy_” initializer in BgpPeer needs to be modified in order to pass the new parameter.
The remainder of the invocations of the RibExportPolicy constructor should receive the new parameter as “false“. There is one invocation in the production code, while initializing an Xmpp peer, and several calls in the test code while initializing test scenarios. There will need to be edited to get the code to compile again.
Now we are probably ready to implement our knob. Following the Test Driven Development methodology the first step is to build our unit test. We want to build a test that has a test case with a route that only has non-private AS route targets and is unaffected by this change; and a test case where a route-target community is stripped from the route advertisement.
The UnitTest code is in the file controller/src/bgp/l3vpn/test/rtarget_strip_private_test.cc.
It works by creating an InetVpnTable and a corresponding RibOut with an export policy where the rtarget_strip_private option is true. We use a mock BgpMessageBuilder to record the attributes that are encoded to the peer. The BgpMessageBuilder interface is called when an update is removed from the queue and needs to be encoded on the wire via either BGP or XML encoding. In this case, our mock records the communities that are advertised to the test peer.
The test cases themselves work by creating a route with a specific list of route targets, adding the route to the table and verifying the list of communities seeing by the message builder interface.
Once this test is compiled, the “Match” test case should fail since we have not yet implemented the functionality. We can do that now:
Rerun the Unit Test. It should now pass.
You can find the code used in this example in the “tutorial” branch of the contrail-controller repository in GitHub.