-
Notifications
You must be signed in to change notification settings - Fork 51
Creating Apps With REST and JSON
The Frenetic Python language bindings communicate to Frenetic through REST and JSON. But you can use this communication channel directly as well. That way you can use any programming language that speaks HTTP and JSON (pretty much any one) to write your Frenetic-based apps.
To show how this works, let's do it from the command line. In one Terminal window start up a sample Mininet network:
frenetic@ubuntu-14.04:~/src/frenetic/examples$ sudo mn --topo=single,2 --controller=remote
*** Creating network
*** Adding controller
Unable to contact the remote controller at 127.0.0.1:6633
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...
*** Starting CLI:
mininet>
In another terminal window, start up Frenetic. The Frenetic HTTP controller listens on port 9000 by default -- you can change this with the --http-port
command line switch.
frenetic@ubuntu-1404: ̃src/frenetic$ frenetic http-controller --verbosity debug
[INFO] Calling create!
[INFO] Current uid: 1000
[INFO] Successfully launched OpenFlow controller with pid 3062
[INFO] Connecting to first OpenFlow server socket
[INFO] Failed to open socket to OpenFlow server: (Unix.Unix_error...
[INFO] Retrying in 1 second
[INFO] Successfully connected to first OpenFlow server socket
[INFO] Connecting to second OpenFlow server socket
[INFO] Successfully connected to second OpenFlow server socket
Here's a file with a simple NatKAT policy, which we write in JSON Syntax:
{
"type":"seq", "pols": [
{ "type":"filter", "pred": { "type": true } },
{ "type": "mod", "header": "location", "value": {"type":"pipe", "name": "my_app"} }
]
}
And in the third window, we'll send commands with curl
vagrant@frenetic:~/src/frenetic/examples$ curl -X POST http://localhost:9000/myapp/update_json -d @template_json.json
The compiled flow-table show up in Frenetic's log:
[DEBUG] Setting up flow table
+-----------------------------------------------------+
| 282574488338432 | Pattern | Action |
|-----------------------------------------------------|
| | Output(Controller(128)) |
+-----------------------------------------------------+
In the Mininet window, type pingall
. The pings won't succeed, but the Packet In messages will be saved in Frenetic's event buffer. To retrieve the waiting events, repeatedly call the REST endpoint /event
:
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":1,"payload":{"buffer":"////////aklBYI66CAYAAQgABgQAAWpJQWCOugoAAAEAAAAAAAAKAAAC","id":256},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":1,"payload":{"buffer":"////////aklBYI66CAYAAQgABgQAAWpJQWCOugoAAAEAAAAAAAAKAAAC","id":257},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":1,"payload":{"buffer":"////////aklBYI66CAYAAQgABgQAAWpJQWCOugoAAAEAAAAAAAAKAAAC","id":258},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":2,"payload":{"buffer":"////////zhqg/FgmCAYAAQgABgQAAc4aoPxYJgoAAAIAAAAAAAAKAAAB","id":259},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":2,"payload":{"buffer":"////////zhqg/FgmCAYAAQgABgQAAc4aoPxYJgoAAAIAAAAAAAAKAAAB","id":260},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
{"type":"packet_in","pipe":"my_app","switch_id":1,"port_id":2,"payload":{"buffer":"////////zhqg/FgmCAYAAQgABgQAAc4aoPxYJgoAAAIAAAAAAAAKAAAB","id":261},"length":42}
vagrant@frenetic:~/src/frenetic/examples$ curl -X GET http://localhost:9000/myapp/event
... hang ...
Note the last call blocks because the pingall
only generated 6 events, and we got all of them. When the next event becomes available, the REST command will unblock.
In REST, calling the URL /event/client_id
will return the JSON data below for a fire event, and block if there are no events available. Each event type with its JSON counterpart is listed below. Each time you call /event
you receive at most one JSON object.
{ "type": "packet_in", "switch_id": 1981745, "port_id": 1, "payload": {
"id": 19283745, "buffer": "AB52DF57B12BF87216345" }}
The presence of the id
attribute means the packet is Buffered. The buffer
attribute is the packet contents encoded in Base64.
{ "type": "port_down", "switch_id": 1981745, "port_id": 1 }
The port_down
event is fired when the port is disconnected, deactivated, reconfigured, or removed from the OpenFlow engine.
{ "type": "port_up", "switch_id": 1981745, "port_id": 1 }
The port_up
event is fired when the port is connected, activated, reconfigured, or assigned
to the OpenFlow engine and is ready to use.
{ "type": "switch_down", "switch_id": 1981745 }
The switch_down
event is fired when the switch is gracefully stopped, or the OpenFlow engine has been stopped.
{ "type": "switch_up", "switch_id": 1981745, "ports": [1,2] }
The switch_up
event is fired when the switch and OpenFlow engine are ready to use. The operational ports connected to OpenFlow are sent in ports.
Frenetic commands are equivalent to OpenFlow controller-to-switch messages. Commands in REST are sent to the listed URL via GET or POST. The JSON data listed is an example request (for POSTs) or response (for GETs).
[ {"switch_id": 1981745, "ports": [1,2] }, {"switch_id": 9435797, "ports": [1] } ]
The current_switches
command retrieves a dictionary of operational, OpenFlow enabled switches and their operational ports. The dictionary key is the DPID of the switch and the value is the list of port numbers for that switch.
{"cache_prepare":"keep", "field_order":"default", "remove_tail_drops": false, "dedup_flows": true,
"optimize": true }
-
cache_prepare
(”empty” or ”keep”, defaults to empty): If keep, keep old policies after calling update command. There is an implicit Union between the old and new policies in the new setup. -
dedup_flows
(boolean, defaults to true): If true, remove any OpenFlow table rules that are exactly alike. -
field_order
(”default”, ”heuristic”, or a list of < separated fields, defaults to heuristic): Set field order priority. On occasion, setting this may reduce the OpenFlow table size. The heuristic setting attempts the optimal ordering based on the fields in the policy. -
optimize
(boolean, defaults to true): If true, attempt to optimize the number of OpenFlow rules. -
remove_tail_drops
(boolean, defaults to false): If true, remove any drop rules from the end of the OpenFlow table. This is necessary on switches like the Dell where the ACL table incorrectly prioritizes itself over all L2 and L3 table rules.
See the Events section above for response formats.
{"switch":1981745, "in_port":1, "actions": [ pol1, pol2 . . . poln ],
"payload": { "id": 19283745, "buffer": "AB52DF57B12BF87216345" } }
pkt_out
, which in many ways is the analogue of the packet_in hook, sending a packet out to the switch for processing.
The Python parameters and REST attributes are the same as their packet_in counterparts. The exception is actions which is a JSON list of NetKAT policies. These are listed in the NetKAT JSON Syntax page.
[ { "port no":1, . . . }, . . . ]
port_stats
retrieves current port-level statistics for a certain switch and port. Sending a port_id of 0 retrieves stats of each operational port on that switch. Statistics attributes include:
-
port_no
Port number -
rx_packets
Number of packets received -
tx_packets
Number of packets transmitted -
rx_bytes
Number of bytes received -
tx_bytes
Number of bytes transmitted -
rx_dropped
Number of packets attempted to receive, but dropped tx dropped Number of packets attempted to transmit, but dropped -
rx_errors
Number of packets errored upon receive -
tx_errors
Number of packets errored upon transmit -
rx_fram_err
Number of packets received with frame errors -
rx_over_err
Number of packets received with buffer overrun errors -
rx_crc_err
Number of packets received with CRC errors collisions Number of collisions detected
{ "packets":1000, "bytes": 8000 }
query
retrieves statistics from a query bucket named label
. This label should have been set
up in a {"type":"mod", field:"port"}
policy.
update
sends a NetKAT policy to Frenetic, which will compile it into OpenFlow flow tables for each connected switch. In REST, the policy itself is the JSON packet representing the policy – generally the outermost envelope is a policy combinator like Union.
See the NetKAT JSON Syntax page for details on how to write a NetKAT policy in JSON.