Getting started with OpenDaylight and Python

I decided to try out my first ODL app using Python, and the REST API. One simple case is to grab flow stats, so that’s what I thought I’d try first. I’m going to tyr and keep track of all my opendaylight scripts on github, feel free to have a look: https://github.com/fredhsu/odl-scripts

First I built up a small network using the very handy tree topolgy that mininet offers:

sudo mn --controller=remote,ip=10.55.17.20 --topo tree,3

This creates a binary tree of depth 3, with two hosts at each leaf node. My ODL controller is running at 10.55.17.20. Now I get a nice diagram on the ODL interface:
odl-topo-1
Next I want to have some traffic get statistics from. So I need to send some pings between a couple hosts. However, this will not work in its current state. I have to add a ‘Subnet Gateway’, which I hope to dive deeper into later, but it basically responds to ARPs, and creates host flow entries in switches. To do this I click on ‘Add Gateway IP Address’, and fill in an unused IP address for the subnet my hosts are on:
odl-add-gw
Now I can ping from host to host using Mininet, notice the familiar delay in the first ping due to ARP:

mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=1 ttl=64 time=1010 ms
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=4.86 ms
64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=0.058 ms

After this the diagram in ODL shows my hosts:
old-hosts-1
Ok, so now we’re all set. I can see the stats that ODL from the web page, this shows me what I’m planning on getting programmatically. Since the web front end uses the REST API, I should be able to get whatever information the website is able to show:
odl-flows-sw1
Now its time to start coding. The docs for the Statistics REST API are here. Using that I’m able to go grab stats for all the flows on the controller. My source code is below:


import httplib2
import json
h = httplib2.Http(".cache")
h.add_credentials('admin', 'admin')
#resp, content = h.request('http://10.55.17.20:8080/controller/nb/v2/statistics/default/flowstats', "GET")
# Updated 8 SEP 2013 to reflect new REST API
resp, content = h.request('http://10.55.17.20:8080/controller/nb/v2/statistics/default/flow', "GET")
allFlowStats = json.loads(content)
flowStats = allFlowStats['flowStatistics']
# These JSON dumps were handy when trying to parse the responses
#print json.dumps(flowStats[0]['flowStat'][1], indent = 2)
#print json.dumps(flowStats[4], indent = 2)
for fs in flowStats:
print "\nSwitch ID : " + fs['node']['id']
print '{0:8} {1:8} {2:5} {3:15}'.format('Count', 'Action', 'Port', 'DestIP')
for aFlow in fs['flowStat']:
count = aFlow['packetCount']
actions = aFlow['flow']['actions']
actionType = ''
actionPort = ''
#print actions
if(type(actions) == type(list())):
actionType = actions[1]['type']
actionPort = actions[1]['port']['id']
else:
actionType = actions['type']
actionPort = actions['port']['id']
dst = aFlow['flow']['match']['matchField'][0]['value']
print '{0:8} {1:8} {2:5} {3:15}'.format(count, actionType, actionPort, dst)

view raw

odl-stats.py

hosted with ❤ by GitHub

You can see I basically pull in all the flows, parse the JSON, and pull out the stats I want, then print it all out to the command line. Here’s the result:

Switch ID : 00:00:00:00:00:00:00:07
Count    Action   Port  DestIP         
0        output   3     10.0.0.1       
0        output   3     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:06
Count    Action   Port  DestIP         
0        output   3     10.0.0.1       
0        output   3     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:05
Count    Action   Port  DestIP         
0        output   3     10.0.0.1       
0        output   3     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:04
Count    Action   Port  DestIP         
0        output   3     10.0.0.1       
0        output   3     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:03
Count    Action   Port  DestIP         
6        output   1     10.0.0.1       
6        output   2     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:02
Count    Action   Port  DestIP         
0        output   1     10.0.0.1       
0        output   1     10.0.0.2       

Switch ID : 00:00:00:00:00:00:00:01
Count    Action   Port  DestIP         
0        output   1     10.0.0.1       
0        output   1     10.0.0.2       

Its not much, but hopefully this serves as a good starting point. I’m going to try and do the same in a couple other languages, just for fun, then work on something more complicated. Any ideas/comments/suggestions are welcome!

40 thoughts on “Getting started with OpenDaylight and Python

  1. vdaylight says:

    Very helpful post. Thanks. Two questions:

    You mention that you have to add a Subnect Gateway in order to allow the hosts to ping each other. What does the gateway do? How does adding it make ping work?

    Also, I am trying to trace the network topology discovery sequence through the controller code. When you specify the –controller param while starting mininet, how does mininet hook into the controller using that? Which API is called and then how does the topology discovery occur.?

    Thanks in advance.

    • From what I understand the gateway allows the controller to respond to ARP requests, and will also insert /32 host flows for the hosts that it knows about.

      When you specify the –controller parameter in mininet it establishes a connection to the controller on port 6633. At that point a whole bunch of stuff is probably triggered on the controller side, but topology discovery happens via sending LLDP packets between the different switches.

      Hope that helps!

  2. JB says:

    Thanks for posting this. I’ve been playing around with OpenDaylight in my spare time. Your blog has been very helpful in understanding the interaction with the controller.

    I attempted this script and get the following error when trying to use the debug command. Would you say this is something with my Python setup? I seem to be making contact with the controller properly as I can verify that by entering an invalid password. I can load the page via a web browser and get the data fed back in xml.

    C:\Python27>python.exe test1.py
    Traceback (most recent call last):
    File “test1.py”, line 10, in
    print json.dumps(flowStats[0])
    KeyError: 0

  3. Andrew says:

    Hi Fred, you’ve got some excellent stuff posted here. I am new to SDN and have been experimenting with OpenDaylight for a little bit now. For the life of me, I have not been able to get any northbound code working at all. Trying to run your script results in this error:

    Traceback (most recent call last):
    File “./odl-stats.py”, line 7, in
    allFlowStats = json.loads(content)
    File “/usr/lib/python2.7/json/__init__.py”, line 326, in loads
    return _default_decoder.decode(s)
    File “/usr/lib/python2.7/json/decoder.py”, line 366, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    File “/usr/lib/python2.7/json/decoder.py”, line 384, in raw_decode
    raise ValueError(“No JSON object could be decoded”)
    ValueError: No JSON object could be decoded

    I am trying to run it directly on the Linux VM on which ODL is running, and I modified the IP address to localhost accordingly. Interestingly, whenever I try to run the script, I see the terminal window that ODL is running in update with a notification that the user “admin” has been authorized for [Network-Admin], so the script seems to be logging in fine, but fails after that.

    Further, I am confused about the OSGI environment as well. You had another post that indicated after executing the run.sh script to start ODL, that you should then get an osgi> prompt which can interact with ODL to load and unload various components. Well, I do not get an osgi prompt at all. After I execute the run script, the terminal window just lists the output of the running ODL process, with no way to interact with it at all. What step am I missing?

    Please continue to blog about your experiences, they are extremely informative!

    • Hi Andrew, thanks for your comment! When running the python code did you have any switches connected to the controller that you were trying to pull the stats from? Also, what is the result if you try going to the stats URL directly in your browser? You might try using a tool such as the ‘Advanced REST client’ plugin for chrome to see what JSON data you’re getting from the controller on that url.

      I’m not sure what you’re missing on the OSGI front, but I also haven’t tried out the latest version of the controller code. Let me update my code and see if something has changed. It is also possible that the REST url has changed since my posts and that would be a reason behind your problem above. What version are you currently using?

      • Andrew says:

        Fred, thank you for the reply! Never mind about the OSGI issue, it turns out I had to press enter within the terminal to have the prompt show up.

        There seems to be something amiss on the REST front, though. Navigating to the URL within my browser asks for the login information of admin/admin. When I log in, I am greeted with an Apache 404 error. Yes, there are four switches connected to the controller via mininet (from another VM).

        That all works fine. I can establish reachability between the switches simply by adding a gateway address. And the OpenDaylight Web GUI works just fine as well. I can view and push flows, see all the statistics, etc…

        As I understand it, the webpage is built using the same statistics data from the URL in question. So how on earth could I be getting a 404, when all the stats are perfectly visible in the web interface?

        I will investigate the REST plugin for Chrome, but I have a feeling it won’t help me in this case since the URL is simply returning a 404…

        Your help is greatly appreciated!

  4. xsited says:

    Fred, thanks for the article. Very helpful.

    Still using the old URLs I added a check incase no stats were registered on the flow to prevent a python error. Something like …

    for fs in flowStats:
    print “\nSwitch ID : ” + fs[‘node’][‘@id’]
    print ‘{0:8} {1:8} {2:5} {3:15}’.format(‘Count’, ‘Action’, ‘Port’, ‘DestIP’)
    if not ‘flowStat’ in fs.values():
    print ” none”
    continue

    • Thanks for your comment. I haven’t had a chance to play around much lately, but I’m thinking that it might be good to put together a post that will incorporate more error handling.

      • xsited says:

        Thanks, Fred. As Andrew pointed out, things have changed explaining why things where not matching up with the online documentation and leaving me with a head scratcher. I am working through the changes now and would be happy to forward my findings.

  5. Fabio says:

    Very helpful post. Thanks. One question:

    After set following command :

    mininet> h1 ping h2

    Debug time in Eclipse I want to change the command to h1 ping h3

    Which class I insert the breakpoint

    • I’m a little confused by your question, the h1 ping h2 command is something that is done in mininet, not the controller. So to change the ping you would just stop the ping in mininet and start a new ping with ‘h1 ping h3’. No breakpoints should be needed.

  6. How would I handle a “packet in” message from ODL in python code ? I have looked at all the blogs relating to “ODL and python” and they all seem to do only static flow pushes. Nothing mentioned about reactive flows after seeing a packet-in. Is it not possible for a python program to register for a packet-in?

      • Thanks for the answer. Not what I was hoping to hear, but I can see that I might need to first capture the packetin within Java code (osgi) and then send it over to the python module over normal sockets or ipc. So now the question is … where can I find an example java code that uses osgi to create a packet-in handler. I looked all over the ODL website but could not see any sample code for this (clearly, I suck at searching or its just not there).

      • Cool. I really appreciate the help, Fred.

        This gives some hope to get started on. The code look’s pretty intimidating at first glance (is there an entire sample code file anywhere that I could reuse rather than just snippets – java is unfamiliar territory to me and I’ll have to figure out things like “what libs to import?” or “what object type is ‘c’ in c.add(createContainerServiceDependency…”? and so on). Clearly, just sending the “RawPacket inPkt” over to python wouldn’t help if there’s no python based ODL API to parse it. I will need to parse the relevant fields in osgi, as shown in your code, and send them in a json over to python. From there, I think I can use the simpler Rest-APIs to interact with ODL.

        Wish the ODL guys have a full fledged python interface on their roadmap.

  7. Pauline MARTIAL says:

    Hi,
    I’m new to OpenDaylight and something seems to be wrong with my Subnet Gateway Configuration (first step above): I cannot ping any 2 hosts even when the gateway is configured. However, if I manually add 2 flow entries linking 2 hosts connected to the same switch, then those 2 hosts are able to ping one another…

    Does anyone know what is wrong with my tests? Is it possible that it has something to do with Maven (I didn’t configure anything about Maven yet…)

    Thank you very much in advance,
    Pauline

    ***************************************************************************************

    – My controller is running on Windows 7 (IP address 10.10.255.52)
    – I’m using Mininet on a Linux VM
    – Command used to create topology: sudo mn –controller=remote,ip=10.10.255.52 –topo tree,3
    – I used the following Gateway IP Address/Mask : 10.0.0.254/24
    – Info printed when I run the command “dump” in Mininet:
    mininet> dump
    RemoteController c0: 10.10.255.52:6633 pid=2101>
    OVSSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=2118>
    OVSSwitch s2: lo:127.0.0.1,s2-eth1:None,s2-eth2:None,s2-eth3:None pid=2123>
    OVSSwitch s3: lo:127.0.0.1,s3-eth1:None,s3-eth2:None,s3-eth3:None pid=2128>
    OVSSwitch s4: lo:127.0.0.1,s4-eth1:None,s4-eth2:None,s4-eth3:None pid=2133>
    OVSSwitch s5: lo:127.0.0.1,s5-eth1:None,s5-eth2:None,s5-eth3:None pid=2138>
    OVSSwitch s6: lo:127.0.0.1,s6-eth1:None,s6-eth2:None,s6-eth3:None pid=2143>
    OVSSwitch s7: lo:127.0.0.1,s7-eth1:None,s7-eth2:None,s7-eth3:None pid=2148>
    Host h1: h1-eth0:10.0.0.1 pid=2108>
    Host h2: h2-eth0:10.0.0.2 pid=2109>
    Host h3: h3-eth0:10.0.0.3 pid=2110>
    Host h4: h4-eth0:10.0.0.4 pid=2111>
    Host h5: h5-eth0:10.0.0.5 pid=2112>
    Host h6: h6-eth0:10.0.0.6 pid=2113>
    Host h7: h7-eth0:10.0.0.7 pid=2114>
    Host h8: h8-eth0:10.0.0.8 pid=2115>

    ***************************************************************************************

    • Hi, sorry for not getting back to you sooner. I don’t think this has anything to do with Maven. It is more likely that you have something misconfigured on the controller, have you changed the flow behavior to only allow static flows?

  8. Venu says:

    I created -–topo –tree 1 using mininet. Soon after adding the GW(unused IP address for the subnet where hosts are on). Hosts started showing up. Perfect.
    But pingall doesn’t work. Once i created 2 flows( input port s1-eth2 output port s1-eth1 AND input port s1-eth1 ouput port s1-eth2) pingall worked.

  9. Arun Adiththan says:

    Hi Fred, you’ve some excellent piece of blogs!! Thanks for your time & efforts. Hopefully as I gain more experience with SDN & ODL, I will contribute in a similar way!

    I am fairly new to SDN concepts & ODL.

    You mentioned in one of the responses here that LLDP is used to construct the topology. I have basic question on the topology information maintained at the ODL controller. Does the controller maintain both physical and logical topology (which basically has subset of nodes from physical topology)? How to query topology information?

    From what I see in the list of Hydrogen Base Edition components, I think Topology Manager is closely related. I would appreciated pointers to any tutorial/programming example links.

    Thanks.

  10. I like the valuable info you provide in your articles.
    I will bookmark your blog and check again here regularly. I am quite certain I will learn many new stuff right here!
    Good luck for the next!

  11. I believe everything published made a ton of
    sense. But, think about this, suppose you added a little content?
    I ain’t suggesting your information is not solid., but
    suppose you added a headline that makes people desire more?
    I mean Getting started with OpenDaylight and Python | My
    Notes is a little vanilla. You could peek at Yahoo’s front page and see how they write post titles to grab viewers interested.
    You might try adding a video or a related picture or two to
    grab people excited about everything’ve got to say. In my opinion, it would bring
    your website a little bit more interesting.

  12. netgear dgn2200 default wireless password says:

    Hello, the whole thing is going sound here and ofcourse every one is sharing
    data, that’s really fine, keep up writing.

  13. java says:

    Hi, all your work is great, and helps to learn different stuff. Please, if you have anything for IDS to implement in SDN, or even DPI for SDN – (Security for SDN). Please post it if you can..
    Thanks.

  14. Geeta says:

    Hi Fred,

    I am trying to learn SDN concepts. I have installed ODL and mininet. I could see YANG UI on ODL GUI. how does that help in controlling the flows and configs of the devices on mininet?

  15. dz dz says:

    Hi, Is it possible if you could have same for the ODL version that use MDSAL … 8181 port. The code is no more compatible with current version of ODL. Hope you could do some example using berliyum or carbon or boron… thank you sir.

Leave a reply to dz dz Cancel reply