Tuesday, August 21, 2012

DjangoFlow - Web UI for the POX OpenFlow controller

The Plan

OpenFlow is a simple concept - an open interface to switch and router hardware. Can we tie this into an open web framework to create a foundation for a really simple Web UI?

The tools

I've been learning how to use Django recently, and it seems like a perfect choice for this task. It's written in Python, so it integrates easily with the POX controller. I've also used virtualenv to make it more portable - this means the project is largely self-contained and can be copied to a new system by simply copying the folder.

Implementation

To start, I created a virtualenv called "djangoflow" on an Ubuntu machine and installed django in it. There are lots of ways to do this - do a google for "django setup virtualenv ubuntu" and you'll get tons of hits.

I made a project called mysite, and an app called flew (NZ english for "flow"), and left that bit for the time being.

The folder structure then looks something like this:

djangoflow
- bin
- include
- lib
- local
- mysite
--- flew
--- mysite

I then did a git clone of the latest build of POX from the NOXREPO github, into the djangoflow folder, and then copied everything from the base mysite folder into the pox folder. The folder structure then looks like:

djangoflow
- bin
- include
- lib
- local
- pox
--- .git
--- flew
--- mysite
--- pox

This is great - now back to Django.

The model that I used was really simple, here's what I've got so far:

from django.db import models

# Create your models here.

class Flow(models.Model):
internalip = models.CharField(max_length=200)
externalip = models.CharField(max_length=200)
idletime = models.IntegerField()
hardtime = models.IntegerField()
def __unicode__(self):
return "Internal: " + self.internalip + ", External: " + self.externalip

class User(models.Model):
name = models.CharField(max_length=200)

class Device(models.Model):
dpid = models.CharField(max_length=200)

We'll only use the Flow model today, the others are for expansion later. To make this work, we'll need to edit the settings in mysite.settings - set up the databases and installed_apps sections as follows:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'flew.db',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    # 'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    'django.contrib.admindocs',
    'flew',
)

Awesome, now run syncdb (check against whatever tutorial you use to see how this is done) and populate the database. Now, fire up the shell and create your first flow:

python manage.py shell
from flew.models import Flow
f = Flow(internalip = "10.1.10.2", externalip = "10.1.20.2", idletime = 300, hardtime = 3600)
f.save()

If that all worked, then you'll have a new entry in your database. We are never going to access the database directly though - we can access all the Django goodness from POX.

Open up pox/forwarding/l2_learning.py and have a look through - if you've used this before, then good, if not, then see what it all does.

I've hijacked this just for this example, but it sets a starting point for any POX-Django integrated tools. Make sure your imports section looks like this:

# import django stuff
from django.core.management import setup_environ
from mysite import settings
setup_environ(settings)
from flew.models import Flow

from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.revent import *
from pox.lib.util import dpidToStr
from pox.lib.util import str_to_bool
from pox.lib.packet import ethernet
from pox.lib.packet import ipv4
import time

Then go down to the __init__ function for LearningSwitch and add this to the end:

    # add new flow by default
    flow1 = Flow.objects.all()[0]
    msg = of.ofp_flow_mod()
    msg.match = of.ofp_match()
    msg.match.dl_type = ethernet.IP_TYPE
    msg.match.nw_src = str(flow1.internalip)
    msg.match.nw_dst = str(flow1.externalip)
    msg.idle_timeout = flow1.idletime
    msg.hard_timeout = flow1.hardtime
    msg.actions.append(of.ofp_action_output(port = 1))
    #msg.buffer_id = event.ofp.buffer_id # 6a
    self.connection.send(msg)

What does this do? It takes the first Flow out of our database, creates a flow to allow traffic from internalip, going to externalip, to go out port 1, which should be pointing at the outside world. This flow will stay in the switch for an hour, or 5 minutes without being triggered - whichever happens first.

Question time

Q: Is it really this easy?
A: Yes. Python is easy, Django is easy, Virtualenv is easy, POX is easy. It's just a little tricky making them all work together - that's what this guide is for.

Q: Why add flows this way when we already have a CLI?
A: Django has a clean admin interface that I haven't covered here (check the setup tutorial on the Django website) - you can set flows in there, and every time a switch connects, it will use those flows.

Q: Could I start using this right now?
A: Totally. If you want your switches to retrieve their configuration from the controller automatically when they start up, you can set a bunch of super specific flows here and roll it out now. If you want something a bit more clever, then you can jump into the Django and POX API and make it happen yourself.

Q: What next?
A: There are two extra models that we didn't use - one for user authentication, and one for managing devices. If your OpenFlow devices connect by SSL (which they should in the real world) then you can create models for them that hold particular flows - this can all be managed via a web interface. As for user authentication, there are millions of ways to do this - you can set privileges for who can set certain flows, make requests that admins can approve - the possibilities are endless!

6 comments:

  1. Cool! Do you have snapshots from the Web UI? I have never seen the Django admin interface... Keep the good posting!

    ReplyDelete
  2. Very informative. I will be putting this to use when I get back home from TIP2013. As I have told others at the conference, yours was the most informative and relevant at the conference so far.

    Kudos!

    Brent

    ReplyDelete
    Replies
    1. Thanks dude, enjoyed the beers and the swim and the company in general :) I'll be totally stoked if people actually start building these in practise, and then go on to build something even better!

      Delete
  3. Hi Sam, I am a starter and have a lot of interest to build a Web UI openflow interface.

    Sorry to ask a easy question.

    I do not know how to do from below. Could you help me ? I appreciate all your help.

    from django.db import models

    # Create your models here.

    class Flow(models.Model):
    internalip = models.CharField(max_length=200)
    externalip = models.CharField(max_length=200)
    idletime = models.IntegerField()
    hardtime = models.IntegerField()
    def __unicode__(self):
    return "Internal: " + self.internalip + ", External: " + self.externalip

    class User(models.Model):
    name = models.CharField(max_length=200)

    class Device(models.Model):
    dpid = models.CharField(max_length=200)


    Thanks

    Talen

    ReplyDelete
  4. Hi Sam, thanks for the post, really opens some possibilities. :)

    Is it fair to say that this are pro-active flow pushes that banks the controller understand the topology including switch IP / port in advance? What are your thoughts on re-active flow pushes based on events? For example, if you listen in for "PacketIn" and react accordingly like a real learning switch. What I am getting stuck on is both the framework and controller has event loops that kind of collides when you put them in the same code; but works pretty clumsy if you make them separate code and ships-in-the-night.

    I am sure it is just me learning and figuring things out (I am using Twisted for my purpose and just started to learn it). But wondering if you have something that is already thought out.

    Another option is to use the messenger service that is already included in POX. I am trying to figure that out as well...

    Thanks for the post again. I enjoyed reading it.

    ReplyDelete
    Replies
    1. The big potential problem with openflow is that there is a major bottleneck between the controller and the switch - in practise you're limited to the order of 10-20 packets per second. The more pro-active you can be, the more you can mitigate the possibility of someone DoSing your switch - if it's totally reactive then it's not difficult for someone to flood the switch with new data that requires a huge amount of communication with the controller - not unlike having a large working set in an operating system that ends up IO bound because it's constantly paging data in and out of RAM

      Delete