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 |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 22 | sys.path.append("../protocol") |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 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 * |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 30 | from parse import * |
| 31 | from netutils import * |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 32 | |
| 33 | class Controller(Thread): |
| 34 | """ |
| 35 | Class abstracting the control interface to the switch. |
| 36 | |
| 37 | For receiving messages, two mechanism will be implemented. First, |
| 38 | query the interface with poll. Second, register to have a |
| 39 | function called by message type. The callback is passed the |
| 40 | message type as well as the raw packet (or message object) |
| 41 | |
| 42 | One of the main purposes of this object is to translate between network |
| 43 | and host byte order. 'Above' this object, things should be in host |
| 44 | byte order. |
| 45 | """ |
| 46 | |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 47 | def __init__(host=HOST_DEFAULT, port=PORT_DEFAULT, passive=1): |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 48 | if (passive): |
| 49 | # FIXME: add error handling |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 50 | self.sock = open_ctrlsocket(host, port) |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 51 | self.clientsock, self.clientaddr = self.sock.accept() |
| 52 | else: |
| 53 | print "Error in controller init: Active cxn not supported" |
| 54 | |
| 55 | def register(self, msg_type, handler): |
| 56 | """ |
| 57 | Register a callback to receive a specific message type. |
| 58 | |
| 59 | Only one handler may be registered for a given message type. |
| 60 | @param msg_type The type of message to receive. May be DEFAULT |
| 61 | for all non-handled packets. |
| 62 | @param handler The function to call when a message of the given |
| 63 | type is received. |
| 64 | """ |
| 65 | print "Controller message handler registration not supported" |
| 66 | |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 67 | def poll(self, exp_msg=None, timeout=RCV_TIMEOUT_DEFAULT): |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 68 | """ |
| 69 | Wait for the next OF message received from the switch. |
| 70 | |
| 71 | @param exp_msg If set, return only when this type of message |
| 72 | is received. |
| 73 | |
| 74 | @param timeout If set, return E_TIMEOUT if mesage is not |
| 75 | received in this time. If set to 0, will not block. |
| 76 | |
| 77 | @retval A triple (msg_type, msg, data) where msg_type is the OpenFlow |
| 78 | message type value OFPT_xxx, msg is a message object (from a |
| 79 | SWIG generated class) appropriate to the message type and data is |
| 80 | a string of any additional information following the |
| 81 | normal message. Note that |
| 82 | many messages do not have classes so ofp_hello is returned which |
| 83 | simply has the header. |
| 84 | The data members in the message are in host endian order. |
| 85 | If a timeout (or other error) occurs, None is returned |
| 86 | """ |
| 87 | while 1: |
| 88 | okay, pkt = rcv_data_from_socket(self.clientsoc, timeout) |
| 89 | if not okay or not pkt: |
| 90 | # FIXME: Check for error |
| 91 | return None, None |
| 92 | # Convert msg to the proper OpenFlow message object |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 93 | hdr = of_header_parse(pkt) |
| 94 | print "DEBUG: msg in. pkt len %d. type %d. length %d" % \ |
| 95 | (len(pkt), hdr.type, hdr.length) |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 96 | |
| 97 | if not exp_msg or (exp_msg and (hdr.type == exp_msg)): |
| 98 | return msg_type, msg |
| 99 | |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 100 | def transact(self, msg, xid=None): |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 101 | """ |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 102 | Run a transaction |
| 103 | |
| 104 | Send the message in msg and wait for a reply with a matching |
| 105 | transaction id. |
| 106 | |
| 107 | @param msg The message to send |
| 108 | @param xid If non None, set the transaction ID of the message; |
| 109 | otherwise use the one already present in the message's header. |
| 110 | |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 111 | """ |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 112 | print "Controller transact not supported" |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 113 | |
| 114 | def message_send(self, msg): |
| 115 | """ |
| 116 | Send the message to the switch |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 117 | |
| 118 | @param msg An OpenFlow message object to be forwarded to the switch. |
| 119 | |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 120 | """ |
Dan Talayco | e37999f | 2010-02-09 15:27:12 -0800 | [diff] [blame] | 121 | pass |
Dan Talayco | 3408952 | 2010-02-07 23:07:41 -0800 | [diff] [blame] | 122 | |
| 123 | def kill(self): |
| 124 | self.clientsock.close() |