Initial oftest skeleton with wrapper generators and pylibopenflow
diff --git a/tools/pylibopenflow/pylib/openflow.py b/tools/pylibopenflow/pylib/openflow.py
new file mode 100644
index 0000000..25945b9
--- /dev/null
+++ b/tools/pylibopenflow/pylib/openflow.py
@@ -0,0 +1,336 @@
+"""This module exports OpenFlow protocol to Python.
+
+(C) Copyright Stanford University
+Date October 2009
+Created by ykk
+"""
+import c2py
+import cheader
+import os
+import socket
+import select
+import struct
+import time
+
+class messages(cheader.cheaderfile,c2py.cstruct2py,c2py.structpacker):
+ """Class to handle OpenFlow messages
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, openflow_headerfile=None):
+ """Initialize with OpenFlow header file
+
+ If filename is not provided, check the environment
+ variable PYLIB_OPENFLOW_HEADER and search for openflow.h
+ """
+ if (openflow_headerfile != None):
+ cheader.cheaderfile.__init__(self, openflow_headerfile)
+ else:
+ #Check environment variable
+ path = os.getenv("PYLIB_OPENFLOW_HEADER")
+ if not path:
+ print "PYLIB_OPENFLOW_HEADER is not set in environment"
+ sys.exit(2)
+ cheader.cheaderfile.__init__(self, path+"/openflow.h")
+ #Initialize cstruct2py
+ c2py.cstruct2py.__init__(self)
+ #Initalize packet
+ c2py.structpacker.__init__(self, "!")
+ ##Cached patterns
+ self.patterns={}
+ for (cstructname, cstruct) in self.structs.items():
+ self.patterns[cstructname] = self.get_pattern(cstruct)
+
+ def get_size(self, ctype):
+ """Get size for ctype or name of type.
+ Return None if ctype is not expanded or
+ type with name is not found.
+ """
+ pattern = self.get_pattern(ctype)
+ if (pattern != None):
+ return c2py.cstruct2py.get_size(self,pattern)
+
+ def get_pattern(self,ctype):
+ """Get pattern string for ctype or name of type.
+ Return None if ctype is not expanded or
+ type with name is not found.
+ """
+ if (isinstance(ctype, str)):
+ #Is name
+ return self.patterns[ctype]
+ else:
+ return c2py.cstruct2py.get_pattern(self, ctype)
+
+ def pack(self, ctype, *arg):
+ """Pack packet accordingly ctype or name of type provided.
+ Return struct packed.
+ """
+ if (isinstance(ctype, str)):
+ return struct.pack(self.prefix+self.patterns[ctype], *arg)
+ else:
+ return c2py.structpacker.pack(self, ctype, *arg)
+
+ def peek_from_front(self, ctype, binaryString, returnDictionary=True):
+ """Unpack packet using front of the packet,
+ accordingly ctype or name of ctype provided.
+
+ Return dictionary of values indexed by arg name,
+ if ctype is known struct/type and returnDictionary is True,
+ else return array of data unpacked.
+ """
+ if (isinstance(ctype,str)):
+ data = c2py.structpacker.peek_from_front(self,
+ self.patterns[ctype],
+ binaryString,
+ returnDictionary)
+ return self.data2dic(self.structs[ctype], data)
+ else:
+ return c2py.structpacker.peek_from_front(self,
+ ctype,
+ binaryString,
+ returnDictionary)
+
+ def unpack_from_front(self, ctype, binaryString, returnDictionary=True):
+ """Unpack packet using front of packet,
+ accordingly ctype or name of ctype provided.
+
+ Return (dictionary of values indexed by arg name,
+ remaining binary string) if ctype is known struct/type
+ and returnDictionary is True,
+ else return (array of data unpacked, remaining binary string).
+ """
+ if (isinstance(ctype,str)):
+ (data, remaining) = c2py.structpacker.unpack_from_front(self,
+ self.patterns[ctype],
+ binaryString,
+ returnDictionary)
+ return (self.data2dic(self.structs[ctype], data), remaining)
+ else:
+ return c2py.structpacker.unpack_from_front(self,
+ ctype,
+ binaryString,
+ returnDictionary)
+
+class connection:
+ """Class to hold a connection.
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, messages, sock=None):
+ """Initialize
+ """
+ ##Reference to socket
+ self.sock = sock
+ ##Internal reference to OpenFlow messages
+ self._messages = messages
+ ##Buffer
+ self.buffer = ""
+ ##Header length for OpenFlow
+ self.__header_length = self._messages.get_size("ofp_header")
+
+ def send(self, msg):
+ """Send bare message (given as binary string)
+ """
+ raise NotImplementedError()
+
+ def structsend(self, ctype, *arg):
+ """Build and send message.
+ """
+ self.send(self._messages.pack(ctype, *arg))
+
+ def receive(self, maxlength=1024):
+ """Receive raw in non-blocking way.
+
+ Return buffer
+ """
+ if (select.select([self.sock],[],[],0)[0]):
+ self.buffer += self.sock.recv(maxlength)
+ return self.buffer
+
+ def buffer_has_msg(self):
+ """Check if buffer has a complete message
+ """
+ #Check at least ofp_header is received
+ if (len(self.buffer) < self.__header_length):
+ return False
+ values = self._messages.peek_from_front("ofp_header", self.buffer)
+ return (len(self.buffer) >= values["length"][0])
+
+ def get_msg(self):
+ """Get message from current buffer
+ """
+ if (self.buffer_has_msg()):
+ values = self._messages.peek_from_front("ofp_header", self.buffer)
+ msg = self.buffer[:values["length"][0]]
+ self.buffer = self.buffer[values["length"][0]:]
+ return msg
+ else:
+ return None
+
+ def msgreceive(self, blocking=False, pollInterval=0.001):
+ """Receive OpenFlow message.
+
+ If non-blocking, can return None.
+ """
+ self.receive()
+ if (self.buffer_has_msg()):
+ return self.get_msg()
+ if (blocking):
+ while (not self.buffer_has_msg()):
+ time.sleep(pollInterval)
+ self.receive()
+ return self.get_msg()
+
+class safeconnection(connection):
+ """OpenFlow connection with safety checks
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, messages, sock=None, version=None,
+ xidstart = 0, autoxid=True):
+ """Initialize with OpenFlow version.
+ """
+ connection.__init__(self, messages, sock)
+ ##OpenFlow version
+ if (version != None):
+ self.version = version
+ else:
+ self.version = messages.get_value("OFP_VERSION")
+ ##xid Counter
+ self.nextxid = xidstart
+ ##Automatic xid
+ self.autoxid = autoxid
+ ##Miss auto xid
+ self.skipautoxid = 0
+
+ def skip_auto_xid(self, n):
+ """Miss automatic xid for the next n packets
+ """
+ self.skipautoxid = n
+
+ def structsend_xid(self, ctype, *arg):
+ """Build and send message, populating header automatically.
+ Type and xid of message is not populated.
+ """
+ self.skipautoxid+=1
+ self.structsend(ctype, *arg)
+
+ def structsend(self, ctype, *arg):
+ """Build and send message, populating header automatically.
+ Type of message is not populated
+ """
+ msg = self._messages.pack(ctype, *arg)
+ self.structsend_raw(msg)
+
+ def structsend_raw(self, msg):
+ """Check ofp_header and ensure correctness before sending.
+ """
+ (dic, remaining) = self._messages.unpack_from_front("ofp_header", msg)
+ #Amend header
+ if (self.version != None):
+ dic["version"][0] = self.version
+ if (self.autoxid and (self.skipautoxid == 0)):
+ dic["xid"][0] = self.nextxid
+ self.nextxid+=1
+ if (self.skipautoxid != 0):
+ self.skipautoxid-=1
+ dic["length"][0] = len(remaining)+8
+ #Send message
+ self.send(self._messages.pack("ofp_header",
+ dic["version"][0],
+ dic["type"][0],
+ dic["length"][0],
+ dic["xid"][0])+\
+ remaining)
+
+class tcpsocket(safeconnection):
+ """Class to hold connection
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, messages, host, port):
+ """Initialize TCP socket to host and port
+ """
+ safeconnection.__init__(self, messages)
+ ##Reference to socket
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((host, port))
+ self.sock.setblocking(False)
+ self.sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 0)
+
+ def __del__(self):
+ """Terminate connection
+ """
+ self.sock.shutdown(1)
+ self.sock.close()
+
+ def send(self, msg):
+ """Send raw message (binary string)
+ """
+ self.sock.sendall(msg)
+
+class connections:
+ """Class to hold multiple connections
+
+ (C) Copyright Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize
+ """
+ ##List of sockets
+ self.__sockets = []
+ ##Dicionary of sockets to connection
+ self.__connections = {}
+
+ def add_connection(self, reference, connect):
+ """Add connection with opaque reference object
+ """
+ if (not isinstance(connect,connection)):
+ raise RuntimeError("Connection must be openflow.connection!")
+ self.__sockets.append(connect.sock)
+ self.__connections[connect.sock] = (reference, connect)
+
+ def receive(self, maxlength=1024):
+ """Receive raw in non-blocking way
+ """
+ read_ready = select.select(self.__sockets,[],[],0)[0]
+ for sock in read_ready:
+ self.__connections[sock][1].receive(maxlength)
+
+ def has_msg(self):
+ """Check if any of the connections has a message
+
+ Return (reference,connection) with message
+ """
+ for sock, refconnect in self.__connections.items():
+ if (refconnect[1].buffer_has_msg()):
+ return refconnect
+ return None
+
+ def msgreceive(self, blocking=False, pollInterval=0.001):
+ """Receive OpenFlow message.
+
+ If non-blocking, can return None.
+ """
+ self.receive()
+ c = self.has_msg()
+ if (c != None):
+ return (c[0],c[1].get_msg())
+ if (blocking):
+ while (c == None):
+ time.sleep(pollInterval)
+ self.receive()
+ c = self.has_msg()
+ else:
+ return (None, None)
+ return (c[0],c[1].get_msg())