Managing Switches with REST and Thrift APIs
Learn how to use APIs for networking in this excerpt from "Building Modern Networks."
December 26, 2017
APIs themselves predate modern networks and computing, but only became useful in networking around the year 2000 when REST was introduced. Since REST has been around longer than Thrift, we will discuss it first.
REST
REST, like other networking APIs, is available both on the local system and remotely. REST is designed to be:
Scalable: RESTful applications must be scalable by utilizing some or all of the following concepts:
Simplifying components: A simple component will scale better than a complex one; complex components should be broken down into multiple simpler ones
Decentralization: This could be done by distributing processing across multiple components
Frequency of operations: The more interactions that happen between the client and server, the more it impacts the performance of the application
High performance: There are a few different ways in which performance can be measured:
Network performance: The application must be adaptable to the style and size of the data it is carrying to not overburden the application
Perceived performance: The application must minimize latency by deciding whether to compress data on the fly, create latency on the server side, or stream all of the data that may cause latency on the client side
Modifiable/evolvable: The application must be evolvable, extensible, customizable, configurable, and reusable
Portable: The application must be portable and return both the information and the structure
Visible: The application must be visible to other applications
Reliable: The application must return data in the format expected as designed within the constraints of the REST design
Simple: By design, all REST implementations must use a uniform interface.
REST has six guiding constraints, all of which must be met for an application to be RESTful:
Client-server: The needs of the user application are separate from the server application. The server and client may store data as they see fit but must transmit data in the requested format.
Stateless: The application must be stateless as the server should not retain any client data from previous interactions with the same client.
Cacheable: The data must define whether it is cacheable or not and the amount of time it can be cached.
Layerable: You must be able to feed the application making the request from the server directly or any intermediary nodes that may have cached the required information without needing to be told so.
Code-on-demand: The server can optionally provide code, such as compiled JavaScript or other programs, to be utilized by the client to extend the functionality of the client.
Uniform interface: Once a developer becomes familiar with one of your APIs, they should be able to follow a similar approach for other APIs.
A uniform interface, as described by REST, must do the following:
Identification of resources: When sending data, the data must be portable and sent in the manner the client requests, if available
Manipulation of data by client: The client must have enough information to be able to modify or delete data
Self-descriptive: Each message must contain the information necessary to process the message, for example, which parser (JSON/XML/so on) to utilize
Hypermedia as the engine of the application state: A REST client after making a request to a URI should be able to utilize links provided by the server to discover the necessary information to fulfill its request
REST utilizes standard operations, such as:
PUT: Sends data to be programmed to the REST agent, replacing or modifying the current data
GET: Requests either a list of URIs or specific data necessary from an agent
POST: Creates a new entry in the dataset or within a dataset
DELETE: Deletes an entry in the dataset or within a dataset
In this book, we will focus on the SnapRoute RESTful API.
Apache Thrift
Apache Thrift (or Facebook Thrift as some refer to it) aims to provide scalable services across different programming languages. Thrift combines a generation engine for code with a software stack to create services that work effectively and efficiently between multiple programming languages, including:
C++: A high-level programming language that is feature-full and object-oriented
Python: A high-level general-purpose programming language
Java: A multi-architecture programming language that allows you to run the same program on multiple different types of computers
Ruby: An object-oriented language patterned after Perl and LISP
JavaScript: A remotely interpreted programming language mostly utilized by web servers and websites
The goal of Thrift was to create a simple interface that could work with any programming language; to this end, the following goals are defined:
Simple: The code should be simple, readable, and free of unnecessary dependencies
Consistent: Language-specific code is kept in extensions, not included in the core
Transparent: It should utilize as many commonalities between programming languages as possible
High performance: Performance is more important than beauty; the code should be functional but not always beautiful
In comparison to REST, Thrift has an Interface Definition Language (IDL), which is programming-language-independent and is publish-subscribe instead of client-server. The publish-subscribe method is the concept where the client subscribes to the server to get the data. The client may only receive some of the data published based on filtering. Neither the client nor the server knows who each other are; it just knows the type of data to expect.
In this book, we will focus on Thrift and how it interfaces with the Facebook Open Switch System (FBOSS).
NEXT Page: SnapRoute: A RESTful API programmable routing stack
SnapRoute is a newly formed company focused on providing an easily automated and functional routing platform. It was built by a group of engineers who had worked on Apple's network. Here we will dive into how an API programmable routing stack works.
As it uses a RESTful API, the commands sent to the switch are done using either POST, GET,OPTIONS, PATCH, or DELETE.
SnapRoute refers to ports on a switch as fpPortX, where X is the number of the front panel (fp) ports. If the port can be broken out (for example, 10 Gx4 for 40 G or 25 Gx4 for 100 G), then there will be a delineator fpPortXsY, where the port number is X and the breakout is referred to as Y. So fpPort1s1 would be the front panel port 1, which is the breakout link 1.
The following diagram shows the general software layout of a switch running SnapRoute's FlexSwitch. We have not included the operating system or any other programs or drivers necessary for the device to operate:
image.png
In order to send the output of commands to SnapRoute's FlexSwitch, use a Python module that formats JSON for better readability. If you don't use something to parse the data, the data will be somewhat readable but hard to understand. For example, if you want to see all the interfaces on the box, send this command:
curl -X GET --header 'Content-Type: application/json' --header 'Accept: application/json' 'http://localhost:8080/public/v1/config/Ports'
Without a parser, you will see something similar to the following output, which is very hard to decipher:
{"ObjectId":"6ec727d1-c2c8-44dc-77fd-d9b1fd6dce4c","Object":{"IntfRef":"fpPort1","IfIndex":145,"Name":"fpPort1","OperState":"DOWN","NumUpEvents":0,"LastUpEventTime":"","NumDownEvents":0}}
If we run the command again, using the Python-based json.tool parser, we get:
curl -X GET --header 'Content-Type: application/json' --header 'Accept: application/json' 'http://localhost:8080/public/v1/config/Ports' | python -m json.tool
{
"CurrentMarker": 0,
"MoreExist": false,
"NextMarker": 0,
"ObjCount": 160,
"Objects": [
{
"Object": {
"AdminState": "DOWN",
"Autoneg": "OFF",
"BreakOutMode": "1x100",
"Description": "",
"Duplex": "Full Duplex",
"EnableFEC": false,
"IfIndex": 145,
"IntfRef": "fpPort1",
"LoopbackMode": "",
"MacAddr": "00:90:fb:55:e5:11",
"MediaType": "Media Type",
"Mtu": 9412,
"PRBSPolynomial": "",
"PRBSRxEnable": false,
"PRBSTxEnable": false,
"PhyIntfType": "KR4",
"Speed": 100000
},
"ObjectId": "6ec727d1-c2c8-44dc-77fd-d9b1fd6dce4c"
},
{
"Object": {
"AdminState": "DOWN",
"Autoneg": "OFF",
"BreakOutMode": "1x100",
"Description": "",
"Duplex": "Full Duplex",
"EnableFEC": false,
"IfIndex": 140,
"IntfRef": "fpPort2",
"LoopbackMode": "",
"MacAddr": "00:90:fb:55:e5:11",
"MediaType": "Media Type",
"Mtu": 9412,
"PRBSPolynomial": "",
"PRBSRxEnable": false,
"PRBSTxEnable": false,
"PhyIntfType": "KR4",
"Speed": 100000
},
"ObjectId": "8029f48f-5b1b-492f-73b7-dc879e386508"
},
The preceding information is much clearer.
Taking it further, to get information about a specific port, say fpPort1, send:
curl -X GET --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{"IntfRef":"fpPort1"}' 'http://localhost:8080/public/v1/config/Port' | python -m json.tool
{
"Object": {
"AdminState": "DOWN",
"Autoneg": "OFF",
"BreakOutMode": "1x100",
... duplicate content omitted (see above output)
"Speed": 100000
},
"ObjectId": "6ec727d1-c2c8-44dc-77fd-d9b1fd6dce4c"
}
Some of the pieces of information you receive are as follows:
The port speed is 100 Gbps
The port breakout mode is 1x100 Gbps (that is, it is not broken out)
In the preceding code, note that we are including the filter:
{"IntfRef":"fpPort1"}
This says we are referring to the interface with the name fpPort1 or front panel port 1.
To enable fpPort1, send:
curl -X PATCH --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{"IntfRef":"fpPort1","AdminState":"UP"}' 'http://localhost:8080/public/v1/config/Port' | python -m json.tool
{
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type,
Accept",
"Access-Control-Allow-Methods": "POST, GET, OPTIONS, PATCH, DELETE",
"Access-Control-Allow-Origin": "*",
"Access-Control-Max_age": "86400",
"ObjectId": "6ec727d1-c2c8-44dc-77fd-d9b1fd6dce4c",
"Result": "Success"
}
Refer to the following code:
{"IntfRef":"fpPort1","AdminState":"UP"}
This is the main piece of information; we are asking the system to turn front panel port 1 up.
This tells us that the call was successful:
"Result": "Success"
Now we can query the port again and see that it is now "AdminState": "UP":
curl -X GET --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{"IntfRef":"fpPort1"}' 'http://localhost:8080/public/v1/config/Port' | python -m json.tool
{
"Object": {
"AdminState": "UP",
"Autoneg": "OFF",
"BreakOutMode": "1x100",
"Description": "",
"Duplex": "Full Duplex",
"EnableFEC": false,
"IfIndex": 145,
"IntfRef": "fpPort1",
"LoopbackMode": "",
"MacAddr": "00:90:fb:55:e5:11",
"MediaType": "Media Type",
"Mtu": 9412,
"PRBSPolynomial": "",
"PRBSRxEnable": false,
"PRBSTxEnable": false,
"PhyIntfType": "KR4",
"Speed": 100000
},
"ObjectId": "6ec727d1-c2c8-44dc-77fd-d9b1fd6dce4c"
}
NEXT Page: More configurations
To configure the speed of the port, you use the same command but substitute Speed with extra data:
Before:
curl -X GET "http://10.7.1.78:8080/public/v1/config/Port?IntfRef=fpPort2" | python -m json.tool
"Object": {
"AdminState": "DOWN",
"Autoneg": "OFF",
"BreakOutMode": "1x100",
"Description": "",
"Duplex": "Full Duplex",
"EnableFEC": false,
"IfIndex": 140,
"IntfRef": "fpPort2",
"LoopbackMode": "",