Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 1 | """ |
| 2 | OpenFlow Test Framework |
| 3 | |
| 4 | Controller class |
| 5 | |
| 6 | Provide the interface to the control channel to the switch under test. |
| 7 | |
| 8 | Class inherits from thread so as to run in background allowing |
| 9 | asynchronous callbacks (if needed, not required). Also supports |
| 10 | polling. |
| 11 | |
| 12 | The controller thread maintains a queue. Incoming messages that |
| 13 | are not handled by a callback function are placed in this queue for |
| 14 | poll calls. |
| 15 | |
| 16 | Callbacks and polling support specifying the message type |
| 17 | |
| 18 | @todo Support transaction semantics via xid |
| 19 | """ |
| 20 | |
| 21 | import sys |
| 22 | sys.path.append("../ofmsg") |
| 23 | import os |
| 24 | import socket |
| 25 | import time |
| 26 | import promisc |
| 27 | from threading import Thread |
| 28 | from threading import Lock |
| 29 | from message import * |
| 30 | |
| 31 | class Controller(Thread): |
| 32 | """ |
| 33 | Class abstracting the control interface to the switch. |
| 34 | |
| 35 | For receiving messages, two mechanism will be implemented. First, |
| 36 | query the interface with poll. Second, register to have a |
| 37 | function called by message type. The callback is passed the |
| 38 | message type as well as the raw packet (or message object) |
| 39 | |
| 40 | One of the main purposes of this object is to translate between network |
| 41 | and host byte order. 'Above' this object, things should be in host |
| 42 | byte order. |
| 43 | """ |
| 44 | |
| 45 | def __init__(port=6633, passive=1): |
| 46 | if (passive): |
| 47 | # FIXME: add error handling |
| 48 | self.sock = open_ctrlsocket() |
| 49 | self.clientsock, self.clientaddr = self.sock.accept() |
| 50 | else: |
| 51 | print "Error in controller init: Active cxn not supported" |
| 52 | |
| 53 | def register(self, msg_type, handler): |
| 54 | """ |
| 55 | Register a callback to receive a specific message type. |
| 56 | |
| 57 | Only one handler may be registered for a given message type. |
| 58 | @param msg_type The type of message to receive. May be DEFAULT |
| 59 | for all non-handled packets. |
| 60 | @param handler The function to call when a message of the given |
| 61 | type is received. |
| 62 | """ |
| 63 | print "Controller message handler registration not supported" |
| 64 | |
| 65 | def poll(self, exp_msg=None, timeout=None): |
| 66 | """ |
| 67 | Wait for the next OF message received from the switch. |
| 68 | |
| 69 | @param exp_msg If set, return only when this type of message |
| 70 | is received. |
| 71 | |
| 72 | @param timeout If set, return E_TIMEOUT if mesage is not |
| 73 | received in this time. If set to 0, will not block. |
| 74 | |
| 75 | @retval A triple (msg_type, msg, data) where msg_type is the OpenFlow |
| 76 | message type value OFPT_xxx, msg is a message object (from a |
| 77 | SWIG generated class) appropriate to the message type and data is |
| 78 | a string of any additional information following the |
| 79 | normal message. Note that |
| 80 | many messages do not have classes so ofp_hello is returned which |
| 81 | simply has the header. |
| 82 | The data members in the message are in host endian order. |
| 83 | If a timeout (or other error) occurs, None is returned |
| 84 | """ |
| 85 | while 1: |
| 86 | okay, pkt = rcv_data_from_socket(self.clientsoc, timeout) |
| 87 | if not okay or not pkt: |
| 88 | # FIXME: Check for error |
| 89 | return None, None |
| 90 | # Convert msg to the proper OpenFlow message object |
| 91 | msg_type, msg = ofpkt.pkt_to_msg(pkt) |
| 92 | print "DEBUG: Got msg type %d of len %d" % (msg_type, len(msg)) |
| 93 | |
| 94 | if not exp_msg or (exp_msg and (hdr.type == exp_msg)): |
| 95 | return msg_type, msg |
| 96 | |
| 97 | def flow_install(self, flow): |
| 98 | """ |
| 99 | Install the flow indicated through the control interface |
| 100 | TBD: We may just use message_send below with ofp_flow_mod objects |
| 101 | @param flow The ofp_flow_mod object to install |
| 102 | """ |
| 103 | |
| 104 | def message_send(self, msg): |
| 105 | """ |
| 106 | Send the message to the switch |
| 107 | @param msg An OpenFlow message object (from a SWIG generated |
| 108 | class) to be forwarded to the switch. The data members of the |
| 109 | object must be in host endian order when pased to message_send. |
| 110 | """ |
| 111 | |
| 112 | def kill(self): |
| 113 | self.clientsock.close() |