Initial oftest skeleton with wrapper generators and pylibopenflow
diff --git a/tools/pylibopenflow/pylib/c2py.py b/tools/pylibopenflow/pylib/c2py.py
new file mode 100644
index 0000000..b699c5e
--- /dev/null
+++ b/tools/pylibopenflow/pylib/c2py.py
@@ -0,0 +1,154 @@
+"""This module converts C types to Python struct pattern string.
+
+Date June 2009
+Created by ykk
+"""
+import cheader
+import struct
+
+class cstruct2py:
+ """Class converts C struct to Python struct pattern string
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize
+ """
+ ##Mapping
+ self.structmap = {}
+ self.structmap["char"] = "c"
+ self.structmap["signed char"] = "b"
+ self.structmap["uint8_t"]=\
+ self.structmap["unsigned char"] = "B"
+ self.structmap["short"] = "h"
+ self.structmap["uint16_t"] =\
+ self.structmap["unsigned short"] = "H"
+ self.structmap["int"] = "i"
+ self.structmap["unsigned int"] = "I"
+ self.structmap["long"] = "l"
+ self.structmap["uint32_t"] =\
+ self.structmap["unsigned long"] = "L"
+ self.structmap["long long"] = "q"
+ self.structmap["uint64_t"] =\
+ self.structmap["unsigned long long"] = "Q"
+ self.structmap["float"] = "f"
+ self.structmap["double"] = "d"
+
+ def get_pattern(self,ctype):
+ """Get pattern string for ctype.
+ Return None if ctype is not expanded.
+ """
+ if (ctype.expanded):
+ if (isinstance(ctype, cheader.cprimitive)):
+ return self.structmap[ctype.typename]
+ elif (isinstance(ctype, cheader.cstruct)):
+ string=""
+ for member in ctype.members:
+ string += self.get_pattern(member)
+ return string
+ elif (isinstance(ctype, cheader.carray)):
+ if (ctype.size == 0):
+ return ""
+ else:
+ string = self.get_pattern(ctype.object)
+ return string * ctype.size
+ return None
+
+ def get_size(self, ctype, prefix="!"):
+ """Return size of struct or pattern specified
+ """
+ if (isinstance(ctype, str)):
+ return struct.calcsize(ctype)
+ elif (isinstance(ctype, cheader.ctype)):
+ return struct.calcsize(prefix + self.get_pattern(ctype))
+ else:
+ return 0
+
+class structpacker:
+ """Pack/unpack packets with ctype.
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, prefix=""):
+ """Initialize with prefix to struct
+ """
+ ##Reference to prefix
+ self.prefix = prefix
+
+ def pack(self, ctype, *arg):
+ """Pack packet accordingly ctype or pattern provided.
+ Return struct packed.
+ """
+ if (isinstance(ctype, str)):
+ return struct.pack(self.prefix+ctype, *arg)
+ elif (isinstance(ctype, cheader.ctype)):
+ return struct.pack(self.prefix+cstruct2py.get_pattern(ctype),
+ *arg)
+ else:
+ return None
+
+ def unpack_from_front(self, ctype, binaryString, returnDictionary=True):
+ """Unpack packet using front of packet,
+ accordingly ctype or pattern provided.
+
+ Return (dictionary of values indexed by arg name,
+ remaining binary string) if ctype is cheader.ctype
+ and returnDictionary is True,
+ else return (array of data unpacked, remaining binary string).
+ """
+ pattern = ""
+ if (isinstance(ctype, str)):
+ pattern = ctype
+ elif (isinstance(ctype, cheader.ctype)):
+ pattern = cstruct2py.get_pattern(ctype)
+ else:
+ return None
+ dsize = struct.calcsize(pattern)
+
+ if (dsize > len(binaryString)):
+ return None
+
+ return (structpacker.peek_from_front(self, pattern, binaryString, returnDictionary),
+ binaryString[dsize:])
+
+ def peek_from_front(self, ctype, binaryString, returnDictionary=True):
+ """Unpack packet using front of packet,
+ accordingly ctype or pattern provided.
+
+ Return dictionary of values indexed by arg name,
+ if ctype is cheader.ctype and returnDictionary is True,
+ else return array of data unpacked.
+ """
+ pattern = self.prefix
+ if (isinstance(ctype, str)):
+ pattern += ctype
+ elif (isinstance(ctype, cheader.ctype)):
+ pattern += cstruct2py.get_pattern(ctype)
+ else:
+ return None
+ dsize = struct.calcsize(pattern)
+ if (dsize > len(binaryString)):
+ return None
+ data = struct.unpack(pattern, binaryString[0:dsize])
+
+ #Return values
+ if (isinstance(ctype, str) or
+ (not returnDictionary)):
+ return data
+ else:
+ return self.data2dic(data, ctype)
+
+ def data2dic(self,ctype,data):
+ """Convert data to dictionary
+ """
+ valDic = {}
+ names = ctype.get_names()
+ for name in names:
+ valDic[name] = []
+ for d in data:
+ name = names.pop(0)
+ valDic[name].append(d)
+ return valDic
+
diff --git a/tools/pylibopenflow/pylib/cheader.py b/tools/pylibopenflow/pylib/cheader.py
new file mode 100644
index 0000000..a23e1eb
--- /dev/null
+++ b/tools/pylibopenflow/pylib/cheader.py
@@ -0,0 +1,434 @@
+"""This module parse and store a C/C++ header file.
+
+Date June 2009
+Created by ykk
+"""
+import re
+from config import *
+
+class textfile:
+ """Class to handle text file.
+
+ Date June 2009
+ Created by ykk
+ """
+ def __init__(self, filename):
+ """Initialize filename with no content.
+ """
+ ##Filename
+ if (isinstance(filename, str)):
+ self.filename = []
+ self.filename.append(filename)
+ else:
+ self.filename = filename
+ ##Content
+ self.content = []
+
+ def read(self):
+ """Read file
+ """
+ for filename in self.filename:
+ fileRef = open(filename, "r")
+ for line in fileRef:
+ self.content.append(line)
+ fileRef.close()
+
+class ctype:
+ """Class to represent types in C
+ """
+ def __init__(self,typename, name=None, expanded=False):
+ """Initialize
+ """
+ ##Name
+ self.name = name
+ ##Type of primitive
+ self.typename = typename
+ ##Expanded
+ self.expanded = expanded
+
+ def expand(self, cheader):
+ """Expand type if applicable
+ """
+ raise NotImplementedError()
+
+ def get_names(self):
+ """Return name of variables
+ """
+ raise NotImplementedError()
+
+class cprimitive(ctype):
+ """Class to represent C primitive
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self,typename, name=None):
+ """Initialize and store primitive
+ """
+ ctype.__init__(self, typename, name, True)
+
+ def __str__(self):
+ """Return string representation
+ """
+ if (self.name == None):
+ return self.typename
+ else:
+ return self.typename+" "+str(self.name)
+
+ def expand(self, cheader):
+ """Expand type if applicable
+ """
+ pass
+
+ def get_names(self):
+ """Return name of variables
+ """
+ namelist = []
+ namelist.append(self.name)
+ return namelist
+
+class cstruct(ctype):
+ """Class to represent C struct
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, typename, name=None):
+ """Initialize struct
+ """
+ ctype.__init__(self, typename, name)
+ ##List of members in struct
+ self.members = []
+
+ def __str__(self):
+ """Return string representation
+ """
+ string = "struct "+self.typename
+ if (self.name != None):
+ string += " "+self.name
+ if (len(self.members) == 0):
+ return string
+ #Add members
+ string +=" {\n"
+ for member in self.members:
+ string += "\t"+str(member)
+ if (not isinstance(member, cstruct)):
+ string += ";"
+ string += "\n"
+ string +="};"
+ return string
+
+ def expand(self, cheader):
+ """Expand struct
+ """
+ self.expanded = True
+ #Expanded each member
+ for member in self.members:
+ if (isinstance(member, cstruct) and
+ (not member.expanded)):
+ try:
+ if (not cheader.structs[member.typename].expanded):
+ cheader.structs[member.typename].expand(cheader)
+ member.members=cheader.structs[member.typename].members[:]
+ member.expanded = True
+ except KeyError:
+ self.expanded=False
+ else:
+ member.expand(cheader)
+
+ def get_names(self):
+ """Return name of variables
+ """
+ namelist = []
+ for member in self.members:
+ if (isinstance(member, cstruct)):
+ tmplist = member.get_names()
+ for item in tmplist:
+ namelist.append(member.name+"."+item)
+ else:
+ namelist.extend(member.get_names())
+ return namelist
+
+
+class carray(ctype):
+ """Class to represent C array
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, typename, name, isPrimitive, size):
+ """Initialize array of object.
+ """
+ ctype.__init__(self, typename, name,
+ (isinstance(size, int) and isPrimitive))
+ ##Object reference
+ if (isPrimitive):
+ self.object = cprimitive(typename, name)
+ else:
+ self.object = cstruct(typename, name)
+ ##Size of array
+ self.size = size
+
+ def __str__(self):
+ """Return string representation
+ """
+ return str(self.object)+"["+str(self.size)+"]"
+
+ def expand(self, cheader):
+ """Expand array
+ """
+ self.expanded = True
+ if (not self.object.expanded):
+ if (isinstance(self.object, cstruct)):
+ cheader.structs[self.object.typename].expand(cheader)
+ self.object.members=cheader.structs[self.object.typename].members[:]
+ else:
+ self.object.expand(cheader)
+
+ if (not isinstance(self.size, int)):
+ val = cheader.get_value(self.size)
+ if (val == None):
+ self.expanded = False
+ else:
+ try:
+ self.size = int(val)
+ except ValueError:
+ self.size = val
+ self.expanded = False
+
+ def get_names(self):
+ """Return name of variables
+ """
+ namelist = []
+ for i in range(0,self.size):
+ namelist.append(self.object.name)
+ return namelist
+
+class ctype_parser:
+ """Class to check c types
+
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize
+ """
+ self.CPrimitives = ["char","signed char","unsigned char",
+ "short","unsigned short",
+ "int","unsigned int",
+ "long","unsigned long",
+ "long long","unsigned long long",
+ "float","double",
+ "uint8_t","uint16_t","uint32_t","uint64_t"]
+
+ def is_primitive(self,type):
+ """Check type given is primitive.
+
+ Return true if valid, and false otherwise
+ """
+ if (type in self.CPrimitives):
+ return True
+ else:
+ return False
+
+ def is_array(self, string):
+ """Check if string declares an array
+ """
+ parts=string.strip().split()
+ if (len(parts) <= 1):
+ return False
+ else:
+ pattern = re.compile("\[.*?\]", re.MULTILINE)
+ values = pattern.findall(string)
+ if (len(values) == 1):
+ return True
+ else:
+ return False
+
+ def parse_array(self, string):
+ """Parse array from string.
+ Return occurrence and name.
+ """
+ pattern = re.compile("\[.*?\]", re.MULTILINE)
+ namepattern = re.compile(".*?\[", re.MULTILINE)
+ values = pattern.findall(string)
+ if (len(values) != 1):
+ return (1,string)
+ else:
+ val = values[0][1:-1]
+ try:
+ sizeval = int(val)
+ except ValueError:
+ if (val==""):
+ sizeval = 0
+ else:
+ sizeval = val
+ return (sizeval,
+ namepattern.findall(string)[0].strip()[0:-1])
+
+ def parse_type(self, string):
+ """Parse string and return cstruct or cprimitive.
+ Else return None
+ """
+ parts=string.strip().split()
+ if (len(parts) >= 2):
+ if (parts[0].strip() == "struct"):
+ typename = " ".join(parts[1:-1])
+ else:
+ typename = " ".join(parts[:-1])
+ (size, name) = self.parse_array(parts[-1])
+ if IGNORE_ZERO_ARRAYS and size == 0:
+ return None
+ #Create appropriate type
+ if (size != 1):
+ #Array
+ return carray(typename, name,
+ self.is_primitive(typename),size)
+ else:
+ #Not array
+ if IGNORE_OFP_HEADER and typename == "ofp_header":
+ return None
+ if (self.is_primitive(typename)):
+ return cprimitive(typename, name)
+ else:
+ return cstruct(typename, name)
+ else:
+ return None
+
+class cheaderfile(textfile):
+ """Class to handle C header file.
+
+ Date June 2009
+ Created by ykk
+ """
+ def __init__(self, filename):
+ """Initialize filename and read from file
+ """
+ textfile.__init__(self,filename)
+ self.read()
+ self.__remove_comments()
+ ##Dictionary of macros
+ self.macros = {}
+ self.__get_macros()
+ ##Dictionary of enumerations
+ self.enums = {}
+ self.enum_values = {}
+ self.__get_enum()
+ self.__get_enum_values()
+ ##Dictionary of structs
+ self.structs = {}
+ self.__get_struct()
+
+ def get_enum_name(self, enum, value):
+ """Return name of variable in enum
+ """
+ for e in self.enums[enum]:
+ if (self.enum_values[e] == value):
+ return e
+
+ def eval_value(self, value):
+ """Evaluate value string
+ """
+ try:
+ return eval(value, self.enum_values)
+ except:
+ return value.strip()
+
+ def get_value(self, name):
+ """Get value for variable name,
+ searching through enum and macros.
+ Else return None
+ """
+ try:
+ return self.enum_values[name]
+ except KeyError:
+ try:
+ return self.macros[name]
+ except KeyError:
+ return None
+
+ def __remove_comments(self):
+ """Remove all comments
+ """
+ fileStr = "".join(self.content)
+ pattern = re.compile("\\\.*?\n", re.MULTILINE)
+ fileStr = pattern.sub("",fileStr)
+ pattern = re.compile(r"/\*.*?\*/", re.MULTILINE|re.DOTALL)
+ fileStr = pattern.sub("",fileStr)
+ pattern = re.compile("//.*$", re.MULTILINE)
+ fileStr = pattern.sub("",fileStr)
+ self.content = fileStr.split('\n')
+
+ def __get_struct(self):
+ """Get all structs
+ """
+ typeparser = ctype_parser()
+ fileStr = "".join(self.content)
+ #Remove attribute
+ attrpattern = re.compile("} __attribute__ \(\((.+?)\)\);", re.MULTILINE)
+ attrmatches = attrpattern.findall(fileStr)
+ for amatch in attrmatches:
+ fileStr=fileStr.replace(" __attribute__ (("+amatch+"));",";")
+ #Find all structs
+ pattern = re.compile("struct[\w\s]*?{.*?};", re.MULTILINE)
+ matches = pattern.findall(fileStr)
+ #Process each struct
+ namepattern = re.compile("struct(.+?)[ {]", re.MULTILINE)
+ pattern = re.compile("{(.+?)};", re.MULTILINE)
+ for match in matches:
+ structname = namepattern.findall(match)[0].strip()
+ if (len(structname) != 0):
+ values = pattern.findall(match)[0].strip().split(";")
+ cstru = cstruct(structname)
+ for val in values:
+ presult = typeparser.parse_type(val)
+ if (presult != None):
+ cstru.members.append(presult)
+ self.structs[structname] = cstru
+ #Expand all structs
+ for (structname, struct) in self.structs.items():
+ struct.expand(self)
+
+ def __get_enum(self):
+ """Get all enumeration
+ """
+ fileStr = "".join(self.content)
+ #Find all enumerations
+ pattern = re.compile("enum[\w\s]*?{.*?}", re.MULTILINE)
+ matches = pattern.findall(fileStr)
+ #Process each enumeration
+ namepattern = re.compile("enum(.+?){", re.MULTILINE)
+ pattern = re.compile("{(.+?)}", re.MULTILINE)
+ for match in matches:
+ values = pattern.findall(match)[0].strip().split(",")
+ #Process each value in enumeration
+ enumList = []
+ value = 0
+ for val in values:
+ if not (val.strip() == ""):
+ valList=val.strip().split("=")
+ enumList.append(valList[0].strip())
+ if (len(valList) == 1):
+ self.enum_values[valList[0].strip()] = value
+ value += 1
+ else:
+ self.enum_values[valList[0].strip()] = self.eval_value(valList[1].strip())
+ self.enums[namepattern.findall(match)[0].strip()] = enumList
+
+ def __get_enum_values(self):
+ """Patch unresolved enum values
+ """
+ for name,enumval in self.enum_values.items():
+ if isinstance(enumval,str):
+ self.enum_values[name] = self.eval_value(enumval)
+
+ def __get_macros(self):
+ """Extract macros
+ """
+ for line in self.content:
+ if (line[0:8] == "#define "):
+ lineList = line[8:].split()
+ if (len(lineList) >= 2):
+ self.macros[lineList[0]] = self.eval_value("".join(lineList[1:]))
+ else:
+ self.macros[lineList[0]] = ""
diff --git a/tools/pylibopenflow/pylib/config.py b/tools/pylibopenflow/pylib/config.py
new file mode 100644
index 0000000..bbf1528
--- /dev/null
+++ b/tools/pylibopenflow/pylib/config.py
@@ -0,0 +1,24 @@
+
+# of_message specific controls
+
+# Do not include any arrays marked [0]
+IGNORE_ZERO_ARRAYS = 1
+
+# Do not include the ofp_header as a member in any structure
+# This allows messages to be consistently generated as:
+# explicit header declaration
+# core member declaration
+# variable length data
+IGNORE_OFP_HEADER = 1
+
+# Generate object equality functions
+GEN_OBJ_EQUALITY = 1
+
+# Generate object show functions
+GEN_OBJ_SHOW = 1
+
+# Generate lists of enum values
+GEN_ENUM_VALUES_LIST = 0
+
+# Generate dictionary of enum strings to values
+GEN_ENUM_DICTIONARY = 1
diff --git a/tools/pylibopenflow/pylib/cpythonize.py b/tools/pylibopenflow/pylib/cpythonize.py
new file mode 100644
index 0000000..6a66d7c
--- /dev/null
+++ b/tools/pylibopenflow/pylib/cpythonize.py
@@ -0,0 +1,533 @@
+"""This module generate Python code for C structs.
+
+Date January 2010
+Created by ykk
+"""
+import cheader
+import c2py
+import datetime
+import struct
+import re
+from config import *
+
+def _space_to(n, str):
+ """
+ Generate a string of spaces to achieve width n given string str
+ If length of str >= n, return one space
+ """
+ spaces = n - len(str)
+ if spaces > 0:
+ return " " * spaces
+ return " "
+
+class rules:
+ """Class that specify rules for pythonization
+
+ Date January 2010
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize rules
+ """
+ ##Default values for members
+ self.default_values = {}
+ #Default values for struct
+ self.struct_default = {}
+ ##What is a tab
+ self.tab = " "
+ ##Macros to exclude
+ self.excluded_macros = []
+ ##Enforce mapping
+ self.enforced_maps = {}
+
+ def get_enforced_map(self, structname):
+ """Get code to enforce mapping
+ """
+ code = []
+ try:
+ mapping = self.enforced_maps[structname]
+ except KeyError:
+ return None
+ for (x,xlist) in mapping:
+ code.append("if (not (self."+x+" in "+xlist+")):")
+ code.append(self.tab+"return (False, \""+x+" must have values from "+xlist+"\")")
+ return code
+
+
+ def get_struct_default(self, structname, fieldname):
+ """Get code to set defaults for member struct
+ """
+ try:
+ return "."+fieldname+self.struct_default[(structname, fieldname)]
+ except KeyError:
+ return None
+
+ def get_default_value(self, structname, fieldname):
+ """Get default value for struct's field
+ """
+ try:
+ return self.default_values[(structname, fieldname)]
+ except KeyError:
+ return 0
+
+ def include_macro(self, name):
+ """Check if macro should be included
+ """
+ return not (name in self.excluded_macros)
+
+class pythonizer:
+ """Class that pythonize C structures
+
+ Date January 2010
+ Created by ykk
+ """
+ def __init__(self, cheaderfile, pyrules = None, tab=" "):
+ """Initialize
+ """
+ ##Rules
+ if (pyrules == None):
+ self.rules = rules()
+ else:
+ self.rules = pyrules
+ ##What is a tab (same as rules)
+ self.tab = str(tab)
+ self.rules.tab = self.tab
+ ##Reference to C header file
+ self.cheader = cheaderfile
+ ##Reference to cstruct2py
+ self.__c2py = c2py.cstruct2py()
+ ##Code for assertion
+ self.__assertcode = []
+
+ def pycode(self,preamble=None):
+ """Return pythonized code
+ """
+ code = []
+ code.append("import struct")
+ code.append("")
+ if (preamble != None):
+ fileRef = open(preamble,"r")
+ for l in fileRef:
+ code.append(l[:-1])
+ fileRef.close()
+ code.append("# Structure definitions")
+ for name,struct in self.cheader.structs.items():
+ code.extend(self.pycode_struct(struct))
+ code.append("")
+ code.append("# Enumerated type definitions")
+ for name,enum in self.cheader.enums.items():
+ code.extend(self.pycode_enum(name,enum))
+ if GEN_ENUM_DICTIONARY:
+ code.extend(self.pycode_enum_map(name,enum))
+ code.append("")
+ code.append("# Values from macro definitions")
+ for name,macro in self.cheader.macros.items():
+ code.extend(self.pycode_macro(name))
+ code.append("")
+ code.append("# Basic structure size definitions.")
+ if IGNORE_OFP_HEADER:
+ code.append("# Does not include ofp_header members.")
+ if IGNORE_ZERO_ARRAYS:
+ code.append("# Does not include variable length arrays.")
+ struct_keys = self.cheader.structs.keys()
+ struct_keys.sort()
+ for name in struct_keys:
+ struct = self.cheader.structs[name]
+ code.append(self.pycode_struct_size(name, struct))
+
+ return code
+
+ def pycode_enum(self, name, enum):
+ """Return Python array for enum
+ """
+ code=[]
+ code.append(name+" = "+str(enum))
+ ev = []
+ for e in enum:
+ v = self.cheader.get_value(e)
+ ev.append(v)
+ code.append(e+"%s= "%_space_to(36,e)+str(v))
+ if GEN_ENUM_VALUES_LIST:
+ code.append(name+"_values = "+str(ev))
+ return code
+
+ def pycode_enum_map(self, name, enum):
+ """Return Python dictionary for enum
+ """
+ code = []
+ code.append(name+"_map = {")
+ first = 1
+ for e in enum:
+ v = self.cheader.get_value(e)
+ if first:
+ prev_e = e
+ prev_v = v
+ first = 0
+ else:
+ code.append(self.tab + "'%s'%s: %s," %
+ (prev_e, _space_to(30, prev_e), prev_v))
+ prev_e = e
+ prev_v = v
+ code.append(self.tab + "'%s'%s: %s" %
+ (prev_e, _space_to(30, prev_e), prev_v))
+ code.append("}")
+ return code
+
+ def pycode_macro(self,name):
+ """Return Python dict for macro
+ """
+ code = []
+ if (self.rules.include_macro(name)):
+ code.append(name+" = "+str(self.cheader.get_value(name)))
+ return code
+
+ def pycode_struct_size(self, name, struct):
+ """Return one liner giving the structure size in bytes
+ """
+ pattern = '!' + self.__c2py.get_pattern(struct)
+ bytes = self.__c2py.get_size(pattern)
+ code = name.upper() + "_BYTES = " + str(bytes)
+ return code
+
+ def pycode_struct(self, struct_in):
+ """Return Python class code given C struct.
+
+ Returns None if struct_in is not cheader.cstruct.
+ Else return list of strings that codes Python class.
+ """
+ if (not isinstance(struct_in, cheader.cstruct)):
+ return None
+
+ code=[]
+ self.__assertcode = []
+ code.extend(self.codeheader(struct_in))
+ code.extend(self.codeinit(struct_in))
+ code.append("")
+ code.extend(self.codeassert(struct_in))
+ code.append("")
+ code.extend(self.codepack(struct_in))
+ code.append("")
+ code.extend(self.codeunpack(struct_in))
+ code.append("")
+ code.extend(self.codelen(struct_in))
+ code.append("")
+ if GEN_OBJ_EQUALITY:
+ code.extend(self.codeeq(struct_in))
+ code.append("")
+ if GEN_OBJ_SHOW:
+ code.extend(self.codeshow(struct_in))
+ code.append("")
+ return code
+
+ def codeheader(self, struct_in):
+ """Return Python code for header
+ """
+ code=[]
+ code.append("class "+struct_in.typename+":")
+ code.append(self.tab+"\"\"\"Automatically generated Python class for "+struct_in.typename)
+ code.append("")
+ code.append(self.tab+"Date "+str(datetime.date.today()))
+ code.append(self.tab+"Created by "+self.__module__+"."+self.__class__.__name__)
+ if IGNORE_OFP_HEADER:
+ code.append(self.tab+"Core structure: Messages do not include ofp_header")
+ if IGNORE_ZERO_ARRAYS:
+ code.append(self.tab+"Does not include var-length arrays")
+ code.append(self.tab+"\"\"\"")
+ return code
+
+ def codeinit(self, struct_in):
+ """Return Python code for init function
+ """
+ code = []
+ code.append(self.tab+"def __init__(self):")
+ code.append(self.tab*2+"\"\"\"Initialize")
+ code.append(self.tab*2+"Declare members and default values")
+ code.append(self.tab*2+"\"\"\"")
+ code.extend(self.codemembers(struct_in,self.tab*2+"self"))
+ return code
+
+ def codemembers(self, struct_in, prepend=""):
+ """Return members of class
+ """
+ code = []
+ for member in struct_in.members:
+ if (isinstance(member, cheader.cstruct)):
+ code.append(prepend+"."+member.name+" = "+member.typename+"()")
+ struct_default = self.rules.get_struct_default(struct_in.typename, member.name)
+ if (struct_default != None):
+ code.append(prepend+struct_default)
+ self.__structassert(member, (prepend+"."+member.name).strip())
+ elif (isinstance(member, cheader.carray)):
+ if (member.typename == "char"):
+ initvalue = "\"\""
+ self.__stringassert(member, (prepend+"."+member.name).strip())
+ else:
+ if (isinstance(member.object, cheader.cprimitive)):
+ initvalue="0"
+ else:
+ initvalue="None"
+ initvalue=(initvalue+",")*member.size
+ initvalue="["+initvalue[:-1]+"]"
+ self.__arrayassert(member, (prepend+"."+member.name).strip())
+ code.append(prepend+"."+member.name+"= "+initvalue)
+ else:
+ code.append(prepend+"."+member.name+" = "+
+ str(self.rules.get_default_value(struct_in.typename, member.name)))
+ return code
+
+ def __structassert(self, cstruct, cstructname):
+ """Return code to check for C array
+ """
+ self.__assertcode.append(self.tab*2+"if(not isinstance("+cstructname+", "+cstruct.typename+")):")
+ self.__assertcode.append(self.tab*3+"return (False, \""+cstructname+" is not class "+cstruct.typename+" as expected.\")")
+
+ def __addassert(self, prefix):
+ code = []
+ code.append(prefix+"if(not self.__assert()[0]):")
+ code.append(prefix+self.tab+"return None")
+ return code
+
+ def __stringassert(self, carray, carrayname):
+ """Return code to check for C array
+ """
+ self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", str)):")
+ self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not string as expected.\")")
+ self.__assertcode.append(self.tab*2+"if(len("+carrayname+") > "+str(carray.size)+"):")
+ self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
+
+ def __arrayassert(self, carray, carrayname):
+ """Return code to check for C array
+ """
+ if (carray.size == 0):
+ return
+ self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", list)):")
+ self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not list as expected.\")")
+ self.__assertcode.append(self.tab*2+"if(len("+carrayname+") != "+str(carray.size)+"):")
+ self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
+
+ def codeassert(self, struct_in):
+ """Return code for sanity checking
+ """
+ code = []
+ code.append(self.tab+"def __assert(self):")
+ code.append(self.tab*2+"\"\"\"Sanity check")
+ code.append(self.tab*2+"\"\"\"")
+ enforce = self.rules.get_enforced_map(struct_in.typename)
+ if (enforce != None):
+ for line in enforce:
+ code.append(self.tab*2+line)
+ code.extend(self.__assertcode)
+ code.append(self.tab*2+"return (True, None)")
+ return code
+
+ def codepack(self, struct_in, prefix="!"):
+ """Return code that pack struct
+ """
+ code = []
+ code.append(self.tab+"def pack(self, assertstruct=True):")
+ code.append(self.tab*2+"\"\"\"Pack message")
+ code.append(self.tab*2+"Packs empty array used as placeholder")
+ code.append(self.tab*2+"\"\"\"")
+ code.append(self.tab*2+"if(assertstruct):")
+ code.extend(self.__addassert(self.tab*3))
+ code.append(self.tab*2+"packed = \"\"")
+ primPattern = ""
+ primMemberNames = []
+ for member in struct_in.members:
+ if (isinstance(member, cheader.cprimitive)):
+ #Primitives
+ primPattern += self.__c2py.structmap[member.typename]
+ primMemberNames.append("self."+member.name)
+ else:
+ (primPattern, primMemberNames) = \
+ self.__codepackprimitive(code, primPattern,
+ primMemberNames, prefix)
+ if (isinstance(member, cheader.cstruct)):
+ #Struct
+ code.append(self.tab*2+"packed += self."+member.name+".pack()")
+ elif (isinstance(member, cheader.carray) and member.typename == "char"):
+ #String
+ code.append(self.tab*2+"packed += self."+member.name+".ljust("+\
+ str(member.size)+",'\\0')")
+ elif (isinstance(member, cheader.carray) and \
+ isinstance(member.object, cheader.cprimitive)):
+ #Array of Primitives
+ expandedarr = ""
+ if (member.size != 0):
+ for x in range(0, member.size):
+ expandedarr += ", self."+member.name+"["+\
+ str(x).strip()+"]"
+ code.append(self.tab*2+"packed += struct.pack(\""+prefix+\
+ self.__c2py.structmap[member.object.typename]*member.size+\
+ "\""+expandedarr+")")
+ else:
+ code.append(self.tab*2+"for i in self."+member.name+":")
+ code.append(self.tab*3+"packed += struct.pack(\""+\
+ prefix+self.__c2py.get_pattern(member.object)+\
+ "\",i)")
+ elif (isinstance(member, cheader.carray) and \
+ isinstance(member.object, cheader.cstruct)):
+ #Array of struct
+ if (member.size != 0):
+ for x in range(0, member.size):
+ code.append(self.tab*2+"packed += self."+member.name+"["+\
+ str(x).strip()+"].pack()")
+ else:
+ code.append(self.tab*2+"for i in self."+member.name+":")
+ code.append(self.tab*3+"packed += i.pack(assertstruct)")
+ #Clear remaining fields
+ (primPattern, primMemberNames) = \
+ self.__codepackprimitive(code, primPattern,
+ primMemberNames, prefix)
+ code.append(self.tab*2+"return packed")
+ return code
+
+ def __codepackprimitive(self, code, primPattern, primMemberNames, prefix):
+ """Return code for packing primitives
+ """
+ if (primPattern != ""):
+ #Clear prior primitives
+ code.append(self.tab*2+"packed += struct.pack(\""+\
+ prefix+primPattern+"\", "+\
+ str(primMemberNames).replace("'","")[1:-1]+")")
+ return ("",[])
+
+ def codelen(self, struct_in):
+ """Return code to return length
+ """
+ pattern = "!" + self.__c2py.get_pattern(struct_in)
+ code = []
+ code.append(self.tab+"def __len__(self):")
+ code.append(self.tab*2+"\"\"\"Return length of message")
+ code.append(self.tab*2+"\"\"\"")
+ code.append(self.tab*2+"l = "+str(self.__c2py.get_size(pattern)))
+ for member in struct_in.members:
+ if (isinstance(member, cheader.carray) and member.size == 0):
+ if (isinstance(member.object, cheader.cstruct)):
+ code.append(self.tab*2+"for i in self."+member.name+":")
+ code.append(self.tab*3+"l += i.length()")
+ else:
+ pattern="!"+self.__c2py.get_pattern(member.object)
+ size=self.__c2py.get_size(pattern)
+ code.append(self.tab*2+"l += len(self."+member.name+")*"+str(size))
+ code.append(self.tab*2+"return l")
+ return code
+
+ def codeeq(self, struct_in):
+ """Return code to return equality comparisons
+ """
+ code = []
+ code.append(self.tab+"def __eq__(self, other):")
+ code.append(self.tab*2+"\"\"\"Return True if self and other have same values")
+ code.append(self.tab*2+"\"\"\"")
+ code.append(self.tab*2+"if type(self) != type(other): return False")
+ for member in struct_in.members:
+ code.append(self.tab*2 + "if self." + member.name + " != other." +
+ member.name + ": return False")
+ code.append(self.tab*2+"return True")
+ code.append("")
+ code.append(self.tab+"def __ne__(self, other): return not self.__eq__(other)")
+ return code
+
+ def codeshow(self, struct_in):
+ """Return code to print basic members of structure
+ """
+ code = []
+ code.append(self.tab+"def show(self, prefix=''):")
+ code.append(self.tab*2+"\"\"\"" + "Print basic members of structure")
+ code.append(self.tab*2+"\"\"\"")
+ for member in struct_in.members:
+ if re.search('pad', member.name):
+ continue
+ elif (isinstance(member, cheader.cstruct)):
+ code.append(self.tab*2 + "print prefix + '" +
+ member.name + ": ' ")
+ code.append(self.tab*2 + "self." + member.name +
+ ".show(prefix + ' ')")
+ elif (isinstance(member, cheader.carray) and
+ not isinstance(member.object, cheader.cprimitive)):
+ code.append(self.tab*2 + "print prefix + '" + member.name +
+ ": ' ")
+ code.append(self.tab*2 + "for obj in self." + member.name + ":")
+ code.append(self.tab*3 + "obj.show(prefix + ' ')")
+ else:
+ code.append(self.tab*2 + "print prefix + '" + member.name +
+ ": ' + str(self." + member.name + ")")
+ return code
+
+ def codeunpack(self, struct_in, prefix="!"):
+ """Return code that unpack struct
+ """
+ pattern = self.__c2py.get_pattern(struct_in)
+ structlen = self.__c2py.get_size(prefix + pattern)
+ code = []
+ code.append(self.tab+"def unpack(self, binaryString):")
+ code.append(self.tab*2+"\"\"\"Unpack message")
+ code.append(self.tab*2+"Do not unpack empty array used as placeholder")
+ code.append(self.tab*2+"since they can contain heterogeneous type")
+ code.append(self.tab*2+"\"\"\"")
+ code.append(self.tab*2+"if (len(binaryString) < "+str(structlen)+"):")
+ code.append(self.tab*3+"return binaryString")
+ offset = 0
+ primPattern = ""
+ primMemberNames = []
+ for member in struct_in.members:
+ if (isinstance(member, cheader.cprimitive)):
+ #Primitives
+ primPattern += self.__c2py.structmap[member.typename]
+ primMemberNames.append("self."+member.name)
+ else:
+ (primPattern, primMemberNames, offset) = \
+ self.__codeunpackprimitive(code, offset, primPattern,
+ primMemberNames, prefix)
+ if (isinstance(member, cheader.cstruct)):
+ #Struct
+ code.append(self.tab*2+"self."+member.name+\
+ ".unpack(binaryString["+str(offset)+":])")
+ pattern = self.__c2py.get_pattern(member)
+ offset += self.__c2py.get_size(prefix+pattern)
+ elif (isinstance(member, cheader.carray) and member.typename == "char"):
+ #String
+ code.append(self.tab*2+"self."+member.name+\
+ " = binaryString["+str(offset)+":"+\
+ str(offset+member.size)+"].replace(\"\\0\",\"\")")
+ offset += member.size
+ elif (isinstance(member, cheader.carray) and \
+ isinstance(member.object, cheader.cprimitive)):
+ #Array of Primitives
+ expandedarr = ""
+ if (member.size != 0):
+ arrpattern = self.__c2py.structmap[member.object.typename]*member.size
+ for x in range(0, member.size):
+ expandedarr += "self."+member.name+"["+\
+ str(x).strip()+"], "
+ code.append(self.tab*2+"("+expandedarr[:-2]+") = struct.unpack_from(\""+\
+ prefix+arrpattern+\
+ "\", binaryString, "+str(offset)+")")
+ offset += struct.calcsize(prefix + arrpattern)
+ elif (isinstance(member, cheader.carray) and \
+ isinstance(member.object, cheader.cstruct)):
+ #Array of struct
+ astructlen = self.__c2py.get_size("!"+self.__c2py.get_pattern(member.object))
+ for x in range(0, member.size):
+ code.append(self.tab*2+"self."+member.name+"["+str(x)+"]"+\
+ ".unpack(binaryString["+str(offset)+":])")
+ offset += astructlen
+ #Clear remaining fields
+ (primPattern, primMemberNames, offset) = \
+ self.__codeunpackprimitive(code, offset, primPattern,
+ primMemberNames, prefix)
+ code.append(self.tab*2+"return binaryString["+str(structlen)+":]");
+ return code
+
+ def __codeunpackprimitive(self, code, offset, primPattern,
+ primMemberNames, prefix):
+ """Return code for unpacking primitives
+ """
+ if (primPattern != ""):
+ #Clear prior primitives
+ code.append(self.tab*2+"("+str(primMemberNames).replace("'","")[1:-1]+\
+ ") = struct.unpack_from(\""+\
+ prefix+primPattern+"\", binaryString, "+str(offset)+")")
+ return ("",[], offset+struct.calcsize(prefix+primPattern))
+
diff --git a/tools/pylibopenflow/pylib/lavi/__init__.py b/tools/pylibopenflow/pylib/lavi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/pylibopenflow/pylib/lavi/__init__.py
diff --git a/tools/pylibopenflow/pylib/lavi/pythonize.py b/tools/pylibopenflow/pylib/lavi/pythonize.py
new file mode 100644
index 0000000..3c150aa
--- /dev/null
+++ b/tools/pylibopenflow/pylib/lavi/pythonize.py
@@ -0,0 +1,74 @@
+"""This module generate Python code for LAVI and messenger
+
+(C) Copyright Stanford University
+Date January 2010
+Created by ykk
+"""
+import cpythonize
+
+class msgrules(cpythonize.rules):
+ """Class that specify rules for pythonization of messenger
+
+ (C) Copyright Stanford University
+ Date January 2010
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize rules
+ """
+ cpythonize.rules.__init__(self)
+ ##Default values for members
+ #Default values for struct
+ ##Macros to exclude
+ self.excluded_macros = ['MESSAGE_HH__']
+ ##Enforce mapping
+ self.enforced_maps['messenger_msg'] = [ ('type','msg_type') ]
+
+class lavirules(msgrules):
+ """Class that specify rules for pythonization of LAVI messages
+
+ (C) Copyright Stanford University
+ Date January 2010
+ Created by ykk
+ """
+ def __init__(self, laviheader):
+ """Initialize rules
+ """
+ msgrules.__init__(self)
+ ##Default values for members
+
+ #Default values for struct
+ self.struct_default[('lavi_poll_message',
+ 'header')] = ".type = "+str(laviheader.get_value('LAVIT_POLL'))
+ self.struct_default[('lavi_poll_stop_message',
+ 'header')] = ".type = "+str(laviheader.get_value('LAVIT_POLL_STOP'))
+ ##Macros to exclude
+ self.excluded_macros = ['LAVI_MSG_HH']
+ ##Enforce mapping
+ self.enforced_maps['lavi_header'] = [ ('type','lavi_type') ]
+
+class msgpythonizer(cpythonize.pythonizer):
+ """Class that pythonize C messenger messages
+
+ (C) Copyright Stanford University
+ Date January 2010
+ Created by ykk
+ """
+ def __init__(self, msgheader):
+ """Initialize
+ """
+ rules = msgrules()
+ cpythonize.pythonizer.__init__(self, msgheader, rules)
+
+class lavipythonizer(cpythonize.pythonizer):
+ """Class that pythonize C messenger messages
+
+ (C) Copyright Stanford University
+ Date December 2009
+ Created by ykk
+ """
+ def __init__(self, msgheader):
+ """Initialize
+ """
+ rules = lavirules(msgheader)
+ cpythonize.pythonizer.__init__(self, msgheader, rules)
diff --git a/tools/pylibopenflow/pylib/of/__init__.py b/tools/pylibopenflow/pylib/of/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/__init__.py
diff --git a/tools/pylibopenflow/pylib/of/msg.py b/tools/pylibopenflow/pylib/of/msg.py
new file mode 100644
index 0000000..8617f56
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/msg.py
@@ -0,0 +1,117 @@
+"""This module parses OpenFlow packets.
+
+Unfortunately, this has to be updated manually for each OpenFlow version
+and packet type. Ugly.
+
+(C) Copyright Stanford University
+Date October 2009
+Created by ykk
+"""
+class parser:
+ """Parser for OpenFlow packets
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, messages):
+ """Initialize
+ """
+ ##Internal reference to OpenFlow messages
+ self.__messages = messages
+
+ def describe(self, packet):
+ """Parse OpenFlow packet and return string description
+ """
+ dic = self.__messages.peek_from_front("ofp_header", packet)
+ desc = self.header_describe(dic)
+ if (dic["type"][0] == self.__messages.get_value("OFPT_HELLO")):
+ pass
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_SET_CONFIG")):
+ desc += "\n\t"+self.switch_config_describe(packet)
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FLOW_MOD")):
+ (fmdic, remaining) = self.__messages.unpack_from_front("ofp_flow_mod", packet)
+ desc += self.flow_mod_describe(fmdic, "\n\t")
+ desc += "\n\twith remaining "+str(len(remaining))+" bytes"
+ else:
+ desc += "\n\tUnparsed..."
+ return desc
+
+ def flow_mod_describe(self, packet, prefix=""):
+ """Parse flow mod and return description
+ """
+ dic = self.__assert_dic(packet, "ofp_flow_mod")
+ if (dic == None):
+ return ""
+ return prefix+\
+ "Flow_mod of command "+self.__messages.get_enum_name("ofp_flow_mod_command", dic["command"][0])+\
+ " idle/hard timeout:"+str(dic["idle_timeout"][0])+"/"+str(dic["hard_timeout"][0])+\
+ self.match_describe(dic, "match.", "\n\t")+\
+ prefix+\
+ "(priority:"+str(dic["priority"][0])+\
+ "/buffer id:"+str(dic["buffer_id"][0])+\
+ "/out port:"+str(dic["out_port"][0])+")"
+
+ def match_describe(self, dic, nameprefix="", prefix=""):
+ """Return description for ofp match
+ """
+ return prefix+"match wildcards:%x" % dic[nameprefix+"wildcards"][0]+\
+ " inport="+str(dic[nameprefix+"in_port"][0])+\
+ prefix+" "+\
+ " ethertype="+str(dic[nameprefix+"dl_type"][0])+\
+ " vlan="+str(dic[nameprefix+"dl_vlan"][0])+\
+ " "+self.eth_describe(dic[nameprefix+"dl_src"])+"->"+\
+ self.eth_describe(dic[nameprefix+"dl_dst"])+\
+ prefix+" "+\
+ " ipproto="+str(dic[nameprefix+"nw_proto"][0])+\
+ " "+self.ip_describe(dic[nameprefix+"nw_src"][0])+\
+ "->"+self.ip_describe(dic[nameprefix+"nw_src"][0])+\
+ prefix+" "+\
+ " transport "+str(dic[nameprefix+"tp_src"][0])+\
+ "->"+str(dic[nameprefix+"tp_dst"][0])
+
+ def switch_config_describe(self, packet):
+ """Parse OpenFlow switch config and return description
+ """
+ dic = self.__assert_dic(packet, "ofp_switch_config")
+ if (dic == None):
+ return ""
+ return "with flag "+str(self.__messages.get_enum_name("ofp_config_flags", dic["flags"][0]))+\
+ " and miss send length "+str(dic["miss_send_len"][0])
+
+ def header_describe(self, packet):
+ """Parse OpenFlow header and return string description
+ """
+ dic = self.__assert_dic(packet, "ofp_header")
+ if (dic == None):
+ return ""
+ return self.__messages.get_enum_name("ofp_type", dic["type"][0])+" packet "+\
+ "(length:"+str(dic["length"][0])+\
+ "/xid:"+str(dic["xid"][0])+")"
+
+ def ip_describe(self, value):
+ """Return string for ip address
+ """
+ desc = ""
+ for i in range(0,4):
+ (value, cv) = divmod(value, 256)
+ desc = str(cv).strip() +"." + desc
+ return desc
+
+ def eth_describe(self, etheraddr):
+ """Return string for ethernet address
+ """
+ desc = ""
+ for value in etheraddr:
+ desc += ":"+("%x" % value).zfill(2)
+ return desc[1:]
+
+ def __assert_dic(self, packet, typename):
+ """Assert and ensure dictionary is given
+ """
+ dic = None
+ if (isinstance(packet, str)):
+ dic = self.__messages.peek_from_front(typename, packet)
+ elif (isinstance(packet, dict)):
+ dic = packet
+ return dic
diff --git a/tools/pylibopenflow/pylib/of/network.py b/tools/pylibopenflow/pylib/of/network.py
new file mode 100644
index 0000000..6765a12
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/network.py
@@ -0,0 +1,191 @@
+"""This module holds the network.
+
+Copyright(C) 2009, Stanford University
+Date October 2009
+Created by ykk
+"""
+import random
+import openflow
+
+class network:
+ """Class holding information about OpenFlow network
+ """
+ def __init__(self):
+ """Initialize
+ """
+ ##List of switches
+ self.switches = []
+ ##Dictionary of links
+ self.links = {}
+ ##Reference to connections
+ self.connections = openflow.connections()
+
+ def add_switch(self, sw):
+ """Add switch to network
+ """
+ self.switches.append(sw)
+ self.connections.add_connection(sw, sw.connection)
+
+ def add_link(self, link):
+ """Add link to network
+ """
+ try:
+ self.links[link.switch1,link.switch2].append(link)
+ except KeyError:
+ self.links[link.switch1,link.switch2] = []
+ self.links[link.switch1,link.switch2].append(link)
+
+class link:
+ """Class to hold information about link
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, switch1, switch2):
+ """Initialize link between specified switches
+ """
+ ##Reference to first switch
+ self.switch1 = switch1
+ ##Reference to second switch
+ self.switch2 = switch2
+
+class switch:
+ """Class holding information about OpenFlow switch
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, miss_send_len=128,
+ sock=None, dpid=None, n_buffers=100, n_tables=1,
+ capability=None):
+ """Initialize switch
+ """
+ ##Socket to controller
+ self.sock = sock
+ ##Datapath id of switch
+ if (dpid != None):
+ self.datapath_id = dpid
+ else:
+ self.datapath_id = random.randrange(1, pow(2,64))
+ ##Number of buffers
+ self.n_buffers = n_buffers
+ ##Number of tables
+ self.n_tables= n_tables
+ ##Capabilities
+ if (isinstance(capability, switch_capabilities)):
+ self.capability = capability
+ else:
+ self.capability = switch_capabilities(miss_send_len)
+ ##Valid Actions
+ self.valid_actions = 0
+ ##List of port
+ self.port = []
+
+class switch_capabilities:
+ """Class to hold switch capabilities
+ """
+ def __init__(self, miss_send_len=128):
+ """Initialize
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ ##Capabilities support by datapath
+ self.flow_stats = True
+ self.table_stats = True
+ self.port_stats = True
+ self.stp = True
+ self.multi_phy_tx = True
+ self.ip_resam = False
+ ##Switch config
+ self.send_exp = None
+ self.ip_frag = 0
+ self.miss_send_len = miss_send_len
+ ##Valid actions
+ self.act_output = True
+ self.act_set_vlan_vid = True
+ self.act_set_vlan_pcp = True
+ self.act_strip_vlan = True
+ self.act_set_dl_src = True
+ self.act_set_dl_dst = True
+ self.act_set_nw_src = True
+ self.act_set_nw_dst = True
+ self.act_set_tp_src = True
+ self.act_set_tp_dst = True
+ self.act_vendor = False
+
+ def get_capability(self, ofmsg):
+ """Return value for uint32_t capability field
+ """
+ value = 0
+ if (self.flow_stats):
+ value += ofmsg.get_value("OFPC_FLOW_STATS")
+ if (self.table_stats):
+ value += ofmsg.get_value("OFPC_TABLE_STATS")
+ if (self.port_stats):
+ value += ofmsg.get_value("OFPC_PORT_STATS")
+ if (self.stp):
+ value += ofmsg.get_value("OFPC_STP")
+ if (self.multi_phy_tx):
+ value += ofmsg.get_value("OFPC_MULTI_PHY_TX")
+ if (self.ip_resam):
+ value += ofmsg.get_value("OFPC_IP_REASM")
+ return value
+
+ def get_actions(self, ofmsg):
+ """Return value for uint32_t action field
+ """
+ value = 0
+ if (self.act_output):
+ value += (1 << (ofmsg.get_value("OFPAT_OUTPUT")+1))
+ if (self.act_set_vlan_vid):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_VLAN_VID")+1))
+ if (self.act_set_vlan_pcp):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_VLAN_PCP")+1))
+ if (self.act_strip_vlan):
+ value += (1 << (ofmsg.get_value("OFPAT_STRIP_VLAN")+1))
+ if (self.act_set_dl_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_DL_SRC")+1))
+ if (self.act_set_dl_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_DL_DST")+1))
+ if (self.act_set_nw_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_NW_SRC")+1))
+ if (self.act_set_nw_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_NW_DST")+1))
+ if (self.act_set_tp_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_TP_SRC")+1))
+ if (self.act_set_tp_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_TP_DST")+1))
+ return value
+
+class port:
+ """Class to hold information about port
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, port_no, stp=(2 << 8), hw_addr=None, name=""):
+ """Initialize
+ """
+ ##Port properties
+ self.port_no = port_no
+ if (hw_addr != None):
+ self.hw_addr = hw_addr
+ else:
+ self.hw_addr = random.randrange(1, pow(2,48))
+ self.name = name
+ ##Port config
+ self.port_down = False
+ self.no_stp = False
+ self.no_recv = False
+ self.no_recv_stp = False
+ self.no_flood = False
+ self.no_fwd = False
+ self.no_packet_in = False
+ #Port state
+ self.link_down = False
+ self.stp = stp
diff --git a/tools/pylibopenflow/pylib/of/pythonize.py b/tools/pylibopenflow/pylib/of/pythonize.py
new file mode 100644
index 0000000..687512b
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/pythonize.py
@@ -0,0 +1,57 @@
+"""This module generate Python code for OpenFlow structs.
+
+(C) Copyright Stanford University
+Date December 2009
+Created by ykk
+"""
+import cpythonize
+from config import *
+
+class rules(cpythonize.rules):
+ """Class that specify rules for pythonization of OpenFlow messages
+
+ (C) Copyright Stanford University
+ Date December 2009
+ Created by ykk
+ """
+ def __init__(self, ofmsg):
+ """Initialize rules
+ """
+ cpythonize.rules.__init__(self)
+ ##Reference to ofmsg
+ self.__ofmsg = ofmsg
+ ##Default values for members
+ self.default_values[('ofp_header','version')] = self.__ofmsg.get_value('OFP_VERSION')
+ self.default_values[('ofp_switch_config',\
+ 'miss_send_len')] = self.__ofmsg.get_value('OFP_DEFAULT_MISS_SEND_LEN')
+ for x in ['ofp_flow_mod','ofp_flow_expired','ofp_flow_stats']:
+ self.default_values[(x,'priority')] = self.__ofmsg.get_value('OFP_DEFAULT_PRIORITY')
+ #Default values for struct
+ self.struct_default[('ofp_flow_mod',
+ 'header')] = ".type = OFPT_FLOW_MOD"
+# 'header')] = ".type = "+str(self.__ofmsg.get_value('OFPT_FLOW_MOD'))
+ ##Macros to exclude
+ self.excluded_macros = ['OFP_ASSERT(EXPR)','OFP_ASSERT(_EXPR)','OFP_ASSERT',
+ 'icmp_type','icmp_code','OFP_PACKED',
+ 'OPENFLOW_OPENFLOW_H']
+ ##Enforce mapping
+ if GEN_ENUM_VALUES_LIST:
+ self.enforced_maps['ofp_header'] = [ ('type','ofp_type_values') ]
+ elif GEN_ENUM_DICTIONARY:
+ self.enforced_maps['ofp_header'] = \
+ [ ('type','ofp_type_map.values()') ]
+
+class pythonizer(cpythonize.pythonizer):
+ """Class that pythonize C structures of OpenFlow messages
+
+ (C) Copyright Stanford University
+ Date December 2009
+ Created by ykk
+ """
+ def __init__(self, ofmsg):
+ """Initialize
+ """
+ ofrules = rules(ofmsg)
+ cpythonize.pythonizer.__init__(self, ofmsg, ofrules)
+ ##Reference to OpenFlow message class
+ self.__ofmsg = ofmsg
diff --git a/tools/pylibopenflow/pylib/of/simu.py b/tools/pylibopenflow/pylib/of/simu.py
new file mode 100644
index 0000000..508b076
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/simu.py
@@ -0,0 +1,144 @@
+"""This module simulates the network.
+
+Copyright(C) 2009, Stanford University
+Date November 2009
+Created by ykk
+"""
+import openflow
+import output
+import of.msg
+import of.network
+
+class network(of.network.network):
+ """Class to simulate OpenFlow network
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize network
+ """
+ of.network.network.__init__(self)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+
+class link(of.network.link):
+ """Class to simulate link
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, switch1, switch2, isUp=True):
+ """Initialize link
+ """
+ of.network.link.__init__(self, switch1, switch2)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+ ##Indicate if link is up
+ self.isUp = isUp
+
+class switch(of.network.switch):
+ """Class to simulate OpenFlow switch
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, messages, controller, port, miss_send_len=128,
+ dpid=None, n_buffers=100, n_tables=1,
+ capability=None, parser=None, connection=None):
+ """Initialize switch
+ """
+ of.network.switch.__init__(self, miss_send_len,
+ None, dpid, n_buffers, n_tables,
+ capability)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+ ##Reference to OpenFlow messages
+ self.__messages = messages
+ ##Reference to connection
+ self.connection = openflow.tcpsocket(messages, controller, port)
+ self.sock = self.connection.sock
+ ##Reference to Parser
+ self.parser = None
+ if (parser == None):
+ self.parser = of.msg.parser(messages)
+ else:
+ self.parser = parser
+
+ def receive_openflow(self, packet):
+ """Switch receive OpenFlow packet, and respond accordingly
+ """
+ dic = self.__messages.peek_from_front("ofp_header", packet)
+ if (dic["type"][0] == self.__messages.get_value("OFPT_HELLO")):
+ output.dbg("Receive hello", self.name)
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_ECHO_REQUEST")):
+ self.reply_echo(dic["xid"][0])
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FEATURES_REQUEST")):
+ self.reply_features(dic["xid"][0])
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FLOW_MOD")):
+ self.handle_flow_mod(packet)
+ else:
+ output.dbg("Unprocessed message "+self.parser.header_describe(dic),
+ self.name)
+
+ def send_hello(self):
+ """Send hello
+ """
+ self.connection.structsend("ofp_hello",
+ 0, self.__messages.get_value("OFPT_HELLO"),
+ 0, 0)
+ output.dbg("Send hello",self.name)
+
+ def send_packet(self, inport, bufferid=None, packet="", xid=0, reason=None):
+ """Send packet in
+ Assume no match as reason, bufferid = 0xFFFFFFFF,
+ and empty packet by default
+ """
+ if (reason == None):
+ reason = self.__messages.get_value("OFPR_NO_MATCH")
+ if (bufferid == None):
+ bufferid = int("0xFFFFFFFF",16)
+ pktin = self.__messages.pack("ofp_packet_in",
+ 0, self.__messages.get_value("OFPT_PACKET_IN"),
+ 0, xid, #header
+ bufferid, len(packet),
+ inport, reason, 0)
+ self.connection.structsend_raw(pktin+packet)
+ output.dbg("Send packet ",self.name)
+
+ def send_echo(self, xid=0):
+ """Send echo
+ """
+ self.connection.structsend_xid("ofp_header",
+ 0, self.__messages.get_value("OFPT_ECHO_REQUEST"),
+ 0, xid)
+ output.dbg("Send echo", self.name)
+
+ def reply_echo(self, xid):
+ """Reply to echo request
+ """
+ self.connection.structsend_xid("ofp_header",
+ 0, self.__messages.get_value("OFPT_ECHO_REPLY"),
+ 0, xid)
+ output.dbg("Reply echo of xid:"+str(xid),self.name)
+
+ def reply_features(self, xid):
+ """Reply to feature request
+ """
+ self.connection.structsend_xid("ofp_switch_features",
+ 0, self.__messages.get_value("OFPT_FEATURES_REPLY"),
+ 0, xid,
+ self.datapath_id, self.n_buffers,
+ self.n_tables,0,0,0,
+ self.capability.get_capability(self.__messages),
+ self.capability.get_actions(self.__messages))
+ output.dbg("Replied features request of xid "+str(xid), self.name)
+
+ def handle_flow_mod(self, packet):
+ """Handle flow mod: just print it here
+ """
+ output.dbg(self.parser.flow_mod_describe(packet), self.name)
+
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())
diff --git a/tools/pylibopenflow/pylib/output.py b/tools/pylibopenflow/pylib/output.py
new file mode 100644
index 0000000..64df4f5
--- /dev/null
+++ b/tools/pylibopenflow/pylib/output.py
@@ -0,0 +1,85 @@
+"""This module implements output printing.
+
+Output are divided into 4 levels and
+can be configured for different verbosity
+
+Copyright(C) 2009, Stanford University
+Date August 2009
+Created by ykk
+"""
+
+##Various output modes
+MODE = {}
+MODE["ERR"] = 0
+MODE["WARN"] = 1
+MODE["INFO"] = 2
+MODE["DBG"] = 3
+
+#Global mode
+global output_mode
+output_mode = None
+
+def set_mode(msg_mode, who=None):
+ """Set the message mode for who
+ If who is None, set global mode
+ """
+ global output_mode
+ if (output_mode == None):
+ output_mode = {}
+ output_mode["global"] = MODE["WARN"]
+ output_mode["DBG"] = []
+ output_mode["INFO"] = []
+ output_mode["WARN"] = []
+
+ #Set global mode
+ if (who == None):
+ output_mode["global"] = MODE[msg_mode]
+ return
+
+ #Individual mode
+ if (msg_mode == "ERR"):
+ return
+ for mode in ["WARN","INFO","DBG"]:
+ if (not (who in mode[mode])):
+ mode[mode].append(who)
+ if (msg_mode == mode):
+ return
+
+def output(msg_mode, msg, who=None):
+ """Print message
+ """
+ global output_mode
+ if (output_mode == None):
+ raise RuntimeException("Output mode is not set")
+
+ #Indicate who string
+ if (who == None):
+ whostr = ""
+ else:
+ whostr = who+":"
+
+ #Print output
+ if (MODE[msg_mode] <= output_mode["global"]):
+ print msg_mode.ljust(4, ' ')+"|"+whostr+msg
+ elif (who in output_mode[msg_mode]):
+ print msg_mode.ljust(4, ' ')+"|"+whostr+msg
+
+def err(msg, who=None):
+ """Print error messages
+ """
+ output("ERR", msg, who)
+
+def warn(msg, who=None):
+ """Print warning messages
+ """
+ output("WARN", msg, who)
+
+def info(msg, who=None):
+ """Print informational messages
+ """
+ output("INFO", msg, who)
+
+def dbg(msg, who=None):
+ """Print debug messages
+ """
+ output("DBG", msg, who)