message: support setting fields with keyword arguments to the constructor

This enables creation of messages in a tree-like fashion.

The basic test module has been converted to the new API.
diff --git a/tests/basic.py b/tests/basic.py
index 0aefe73..43b7de4 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -53,8 +53,7 @@
     Test echo response with short string data
     """
     def runTest(self):
-        request = message.echo_request()
-        request.data = 'OpenFlow Will Rule The World'
+        request = message.echo_request(data='OpenFlow Will Rule The World')
         response, pkt = self.controller.transact(request)
         self.assertTrue(response is not None,
                         "Did not get echo reply (with data)")
@@ -203,9 +202,8 @@
                (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
 
                logging.info("PKT OUT test with %s, port %s" % (opt, dp_port))
-               msg = message.packet_out()
-               msg.in_port = ofp.OFPP_NONE
-               msg.data = str(outpkt)
+               msg = message.packet_out(in_port=ofp.OFPP_NONE,
+                                        data=str(outpkt))
                act = action.action_output()
                act.port = dp_port
                msg.actions.add(act)
@@ -258,9 +256,8 @@
                dp_ports = of_ports[0:num_ports]
                logging.info("PKT OUT test with " + opt +
                                  ", ports " + str(dp_ports))
-               msg = message.packet_out()
-               msg.in_port = ofp.OFPP_NONE
-               msg.data = str(outpkt)
+               msg = message.packet_out(in_port=ofp.OFPP_NONE,
+                                        data=str(outpkt))
                act = action.action_output()
                for i in range(0,num_ports):
                   act.port = dp_ports[i]
@@ -289,9 +286,8 @@
         self.controller.message_send(request)
         
         logging.info("Sending flow request")
-        request = message.flow_stats_request()
-        request.out_port = ofp.OFPP_NONE
-        request.table_id = 0xff
+        request = message.flow_stats_request(out_port=ofp.OFPP_NONE,
+                                             table_id=0xff)
         request.match.wildcards = 0 # ofp.OFPFW_ALL
         response, pkt = self.controller.transact(request)
         self.assertTrue(response is not None,
diff --git a/tools/munger/scripts/message_gen.py b/tools/munger/scripts/message_gen.py
index 1c627c1..34036d3 100644
--- a/tools/munger/scripts/message_gen.py
+++ b/tools/munger/scripts/message_gen.py
@@ -296,7 +296,7 @@
     _p1('"""')
 
     print
-    _p1("def __init__(self):")
+    _p1("def __init__(self, **kwargs):")
     if has_core_members:
         _p2(parent + ".__init__(self)")
     _p2("self.header = ofp_header()")
@@ -308,6 +308,11 @@
             _p2('self.' + list_var + ' = ' + list_type + '()')
     if has_string:
         _p2('self.data = ""')
+    _p2('for (k, v) in kwargs.items():')
+    _p3('if hasattr(self, k):')
+    _p4('setattr(self, k, v)')
+    _p3('else:')
+    _p4('raise NameError("field %s does not exist in %s" % (k, self.__class__))')
 
     print """
 
@@ -528,12 +533,17 @@
     \"""
     Wrapper class for --TYPE-- stats request message
     \"""
-    def __init__(self):
+    def __init__(self, **kwargs):
         self.header = ofp_header()
         ofp_stats_request.__init__(self)
         ofp_--TYPE--_stats_request.__init__(self)
         self.header.type = OFPT_STATS_REQUEST
         self.type = --STATS_NAME--
+        for (k, v) in kwargs.items():
+            if hasattr(self, k):
+                setattr(self, k, v)
+            else:
+                raise NameError("field %s does not exist in %s" % (k, self.__class__))
 
     def pack(self, assertstruct=True):
         self.header.length = len(self)