VOL-1097 : Ofagent integration for voltha 2.0

- Created a common location for python based components
- Adjusted the ofagent component to interact with voltha 2.0
- Added streaming rpc methods for rcv/send of packets to voltha api
- Adjusted voltha.proto

Change-Id: I47fb7b80878ead060b4b42bd16cb4f8aa384fdb6
diff --git a/common/core/northbound/grpc/default_api_handler.go b/common/core/northbound/grpc/default_api_handler.go
index 45e8f36..f935462 100644
--- a/common/core/northbound/grpc/default_api_handler.go
+++ b/common/core/northbound/grpc/default_api_handler.go
@@ -261,3 +261,32 @@
 	log.Debugw("SelfTest-request", log.Fields{"id": id})
 	return nil, errors.New("UnImplemented")
 }
+
+func (handler *DefaultAPIHandler) StreamPacketsOut(packetsOut voltha.VolthaService_StreamPacketsOutServer) error {
+	log.Debugw("StreamPacketsOut-request", log.Fields{"packetsOut": packetsOut})
+	return errors.New("UnImplemented")
+}
+
+func (handler *DefaultAPIHandler) ReceivePacketsIn(
+	empty *empty.Empty,
+	packetsIn voltha.VolthaService_ReceivePacketsInServer,
+) error {
+	log.Debugw("ReceivePacketsIn-request", log.Fields{"packetsIn": packetsIn})
+	return errors.New("UnImplemented")
+}
+
+func (handler *DefaultAPIHandler) ReceiveChangeEvents(
+	empty *empty.Empty,
+	changeEvents voltha.VolthaService_ReceiveChangeEventsServer,
+) error {
+	log.Debugw("ReceiveChangeEvents-request", log.Fields{"changeEvents": changeEvents})
+	return errors.New("UnImplemented")
+}
+
+func (handler *DefaultAPIHandler) Subscribe(
+	ctx context.Context,
+	ofAgent *voltha.OfAgentSubscriber,
+) (*voltha.OfAgentSubscriber, error) {
+	log.Debugw("Subscribe-request", log.Fields{"ofAgent": ofAgent})
+	return nil, errors.New("UnImplemented")
+}
diff --git a/protos/voltha.proto b/protos/voltha.proto
index 6d37329..7574dd2 100644
--- a/protos/voltha.proto
+++ b/protos/voltha.proto
@@ -452,6 +452,20 @@
         };
     }
 
+    // Stream control packets to the dataplane
+    rpc StreamPacketsOut (stream openflow_13.PacketOut) returns (google.protobuf.Empty) {
+        // This does not have an HTTP representation
+    }
+
+    // Receive control packet stream
+    rpc ReceivePacketsIn (google.protobuf.Empty) returns (stream openflow_13.PacketIn) {
+        // This does not have an HTTP representation
+    }
+
+    rpc ReceiveChangeEvents (google.protobuf.Empty) returns (stream openflow_13.ChangeEvent) {
+        // This does not have an HTTP representation
+    }
+
     rpc CreateAlarmFilter(AlarmFilter) returns(AlarmFilter) {
         option (google.api.http) = {
             post: "/api/v1/alarm_filters"
@@ -495,5 +509,8 @@
             post: "/api/v1/devices/{id}/self_test"
         };
     }
+
+    rpc Subscribe (OfAgentSubscriber) returns (OfAgentSubscriber) {
+    }
 }
 
diff --git a/adapters/Makefile b/python/adapters/Makefile
similarity index 100%
rename from adapters/Makefile
rename to python/adapters/Makefile
diff --git a/adapters/README.md b/python/adapters/README.md
similarity index 100%
rename from adapters/README.md
rename to python/adapters/README.md
diff --git a/adapters/__init__.py b/python/adapters/__init__.py
similarity index 100%
rename from adapters/__init__.py
rename to python/adapters/__init__.py
diff --git a/adapters/common/__init__.py b/python/adapters/common/__init__.py
similarity index 100%
rename from adapters/common/__init__.py
rename to python/adapters/common/__init__.py
diff --git a/adapters/common/event_bus.py b/python/adapters/common/event_bus.py
similarity index 100%
rename from adapters/common/event_bus.py
rename to python/adapters/common/event_bus.py
diff --git a/adapters/common/frameio/__init__.py b/python/adapters/common/frameio/__init__.py
similarity index 100%
rename from adapters/common/frameio/__init__.py
rename to python/adapters/common/frameio/__init__.py
diff --git a/adapters/common/frameio/frameio.py b/python/adapters/common/frameio/frameio.py
similarity index 100%
rename from adapters/common/frameio/frameio.py
rename to python/adapters/common/frameio/frameio.py
diff --git a/adapters/common/frameio/third_party/__init__.py b/python/adapters/common/frameio/third_party/__init__.py
similarity index 100%
rename from adapters/common/frameio/third_party/__init__.py
rename to python/adapters/common/frameio/third_party/__init__.py
diff --git a/adapters/common/frameio/third_party/oftest/LICENSE b/python/adapters/common/frameio/third_party/oftest/LICENSE
similarity index 100%
rename from adapters/common/frameio/third_party/oftest/LICENSE
rename to python/adapters/common/frameio/third_party/oftest/LICENSE
diff --git a/adapters/common/frameio/third_party/oftest/README.md b/python/adapters/common/frameio/third_party/oftest/README.md
similarity index 100%
rename from adapters/common/frameio/third_party/oftest/README.md
rename to python/adapters/common/frameio/third_party/oftest/README.md
diff --git a/adapters/common/frameio/third_party/oftest/__init__.py b/python/adapters/common/frameio/third_party/oftest/__init__.py
similarity index 100%
rename from adapters/common/frameio/third_party/oftest/__init__.py
rename to python/adapters/common/frameio/third_party/oftest/__init__.py
diff --git a/adapters/common/frameio/third_party/oftest/afpacket.py b/python/adapters/common/frameio/third_party/oftest/afpacket.py
similarity index 100%
rename from adapters/common/frameio/third_party/oftest/afpacket.py
rename to python/adapters/common/frameio/third_party/oftest/afpacket.py
diff --git a/adapters/common/frameio/third_party/oftest/netutils.py b/python/adapters/common/frameio/third_party/oftest/netutils.py
similarity index 100%
rename from adapters/common/frameio/third_party/oftest/netutils.py
rename to python/adapters/common/frameio/third_party/oftest/netutils.py
diff --git a/adapters/common/manhole.py b/python/adapters/common/manhole.py
similarity index 100%
rename from adapters/common/manhole.py
rename to python/adapters/common/manhole.py
diff --git a/adapters/common/openflow/__init__.py b/python/adapters/common/openflow/__init__.py
similarity index 100%
rename from adapters/common/openflow/__init__.py
rename to python/adapters/common/openflow/__init__.py
diff --git a/adapters/common/openflow/utils.py b/python/adapters/common/openflow/utils.py
similarity index 100%
rename from adapters/common/openflow/utils.py
rename to python/adapters/common/openflow/utils.py
diff --git a/adapters/common/structlog_setup.py b/python/adapters/common/structlog_setup.py
similarity index 100%
rename from adapters/common/structlog_setup.py
rename to python/adapters/common/structlog_setup.py
diff --git a/adapters/common/utils/__init__.py b/python/adapters/common/utils/__init__.py
similarity index 100%
rename from adapters/common/utils/__init__.py
rename to python/adapters/common/utils/__init__.py
diff --git a/adapters/common/utils/asleep.py b/python/adapters/common/utils/asleep.py
similarity index 100%
rename from adapters/common/utils/asleep.py
rename to python/adapters/common/utils/asleep.py
diff --git a/adapters/common/utils/consulhelpers.py b/python/adapters/common/utils/consulhelpers.py
similarity index 100%
rename from adapters/common/utils/consulhelpers.py
rename to python/adapters/common/utils/consulhelpers.py
diff --git a/adapters/common/utils/deferred_utils.py b/python/adapters/common/utils/deferred_utils.py
similarity index 100%
rename from adapters/common/utils/deferred_utils.py
rename to python/adapters/common/utils/deferred_utils.py
diff --git a/adapters/common/utils/dockerhelpers.py b/python/adapters/common/utils/dockerhelpers.py
similarity index 100%
rename from adapters/common/utils/dockerhelpers.py
rename to python/adapters/common/utils/dockerhelpers.py
diff --git a/adapters/common/utils/grpc_utils.py b/python/adapters/common/utils/grpc_utils.py
similarity index 100%
rename from adapters/common/utils/grpc_utils.py
rename to python/adapters/common/utils/grpc_utils.py
diff --git a/adapters/common/utils/id_generation.py b/python/adapters/common/utils/id_generation.py
similarity index 100%
rename from adapters/common/utils/id_generation.py
rename to python/adapters/common/utils/id_generation.py
diff --git a/adapters/common/utils/indexpool.py b/python/adapters/common/utils/indexpool.py
similarity index 100%
rename from adapters/common/utils/indexpool.py
rename to python/adapters/common/utils/indexpool.py
diff --git a/adapters/common/utils/json_format.py b/python/adapters/common/utils/json_format.py
similarity index 100%
rename from adapters/common/utils/json_format.py
rename to python/adapters/common/utils/json_format.py
diff --git a/adapters/common/utils/message_queue.py b/python/adapters/common/utils/message_queue.py
similarity index 100%
rename from adapters/common/utils/message_queue.py
rename to python/adapters/common/utils/message_queue.py
diff --git a/adapters/common/utils/nethelpers.py b/python/adapters/common/utils/nethelpers.py
similarity index 100%
rename from adapters/common/utils/nethelpers.py
rename to python/adapters/common/utils/nethelpers.py
diff --git a/adapters/common/utils/ordered_weakvalue_dict.py b/python/adapters/common/utils/ordered_weakvalue_dict.py
similarity index 100%
rename from adapters/common/utils/ordered_weakvalue_dict.py
rename to python/adapters/common/utils/ordered_weakvalue_dict.py
diff --git a/adapters/common/utils/registry.py b/python/adapters/common/utils/registry.py
similarity index 100%
rename from adapters/common/utils/registry.py
rename to python/adapters/common/utils/registry.py
diff --git a/adapters/docker/Dockerfile.adapter_ponsim_olt b/python/adapters/docker/Dockerfile.adapter_ponsim_olt
similarity index 100%
rename from adapters/docker/Dockerfile.adapter_ponsim_olt
rename to python/adapters/docker/Dockerfile.adapter_ponsim_olt
diff --git a/adapters/docker/Dockerfile.adapter_ponsim_onu b/python/adapters/docker/Dockerfile.adapter_ponsim_onu
similarity index 100%
rename from adapters/docker/Dockerfile.adapter_ponsim_onu
rename to python/adapters/docker/Dockerfile.adapter_ponsim_onu
diff --git a/adapters/docker/Dockerfile.base b/python/adapters/docker/Dockerfile.base
similarity index 100%
rename from adapters/docker/Dockerfile.base
rename to python/adapters/docker/Dockerfile.base
diff --git a/adapters/docker/Dockerfile.protoc b/python/adapters/docker/Dockerfile.protoc
similarity index 100%
rename from adapters/docker/Dockerfile.protoc
rename to python/adapters/docker/Dockerfile.protoc
diff --git a/adapters/docker/Dockerfile.protos b/python/adapters/docker/Dockerfile.protos
similarity index 100%
rename from adapters/docker/Dockerfile.protos
rename to python/adapters/docker/Dockerfile.protos
diff --git a/adapters/docker/config/Makefile.protos b/python/adapters/docker/config/Makefile.protos
similarity index 100%
rename from adapters/docker/config/Makefile.protos
rename to python/adapters/docker/config/Makefile.protos
diff --git a/adapters/env.sh b/python/adapters/env.sh
similarity index 100%
rename from adapters/env.sh
rename to python/adapters/env.sh
diff --git a/adapters/iadapter.py b/python/adapters/iadapter.py
similarity index 100%
rename from adapters/iadapter.py
rename to python/adapters/iadapter.py
diff --git a/adapters/interface.py b/python/adapters/interface.py
similarity index 100%
rename from adapters/interface.py
rename to python/adapters/interface.py
diff --git a/adapters/kafka/__init__.py b/python/adapters/kafka/__init__.py
similarity index 100%
rename from adapters/kafka/__init__.py
rename to python/adapters/kafka/__init__.py
diff --git a/adapters/kafka/adapter_proxy.py b/python/adapters/kafka/adapter_proxy.py
similarity index 100%
rename from adapters/kafka/adapter_proxy.py
rename to python/adapters/kafka/adapter_proxy.py
diff --git a/adapters/kafka/adapter_request_facade.py b/python/adapters/kafka/adapter_request_facade.py
similarity index 100%
rename from adapters/kafka/adapter_request_facade.py
rename to python/adapters/kafka/adapter_request_facade.py
diff --git a/adapters/kafka/container_proxy.py b/python/adapters/kafka/container_proxy.py
similarity index 100%
rename from adapters/kafka/container_proxy.py
rename to python/adapters/kafka/container_proxy.py
diff --git a/adapters/kafka/core_proxy.py b/python/adapters/kafka/core_proxy.py
similarity index 100%
rename from adapters/kafka/core_proxy.py
rename to python/adapters/kafka/core_proxy.py
diff --git a/adapters/kafka/event_bus_publisher.py b/python/adapters/kafka/event_bus_publisher.py
similarity index 100%
rename from adapters/kafka/event_bus_publisher.py
rename to python/adapters/kafka/event_bus_publisher.py
diff --git a/adapters/kafka/kafka_inter_container_library.py b/python/adapters/kafka/kafka_inter_container_library.py
similarity index 100%
rename from adapters/kafka/kafka_inter_container_library.py
rename to python/adapters/kafka/kafka_inter_container_library.py
diff --git a/adapters/kafka/kafka_proxy.py b/python/adapters/kafka/kafka_proxy.py
similarity index 100%
rename from adapters/kafka/kafka_proxy.py
rename to python/adapters/kafka/kafka_proxy.py
diff --git a/adapters/ponsim_olt/VERSION b/python/adapters/ponsim_olt/VERSION
similarity index 100%
rename from adapters/ponsim_olt/VERSION
rename to python/adapters/ponsim_olt/VERSION
diff --git a/adapters/ponsim_olt/__init__.py b/python/adapters/ponsim_olt/__init__.py
similarity index 100%
rename from adapters/ponsim_olt/__init__.py
rename to python/adapters/ponsim_olt/__init__.py
diff --git a/adapters/ponsim_olt/main.py b/python/adapters/ponsim_olt/main.py
similarity index 100%
rename from adapters/ponsim_olt/main.py
rename to python/adapters/ponsim_olt/main.py
diff --git a/adapters/ponsim_olt/ponsim_olt.py b/python/adapters/ponsim_olt/ponsim_olt.py
similarity index 100%
rename from adapters/ponsim_olt/ponsim_olt.py
rename to python/adapters/ponsim_olt/ponsim_olt.py
diff --git a/adapters/ponsim_olt/ponsim_olt.yml b/python/adapters/ponsim_olt/ponsim_olt.yml
similarity index 100%
rename from adapters/ponsim_olt/ponsim_olt.yml
rename to python/adapters/ponsim_olt/ponsim_olt.yml
diff --git a/adapters/ponsim_onu/VERSION b/python/adapters/ponsim_onu/VERSION
similarity index 100%
rename from adapters/ponsim_onu/VERSION
rename to python/adapters/ponsim_onu/VERSION
diff --git a/adapters/ponsim_onu/__init__.py b/python/adapters/ponsim_onu/__init__.py
similarity index 100%
rename from adapters/ponsim_onu/__init__.py
rename to python/adapters/ponsim_onu/__init__.py
diff --git a/adapters/ponsim_onu/main.py b/python/adapters/ponsim_onu/main.py
similarity index 100%
rename from adapters/ponsim_onu/main.py
rename to python/adapters/ponsim_onu/main.py
diff --git a/adapters/ponsim_onu/ponsim_onu.py b/python/adapters/ponsim_onu/ponsim_onu.py
similarity index 100%
rename from adapters/ponsim_onu/ponsim_onu.py
rename to python/adapters/ponsim_onu/ponsim_onu.py
diff --git a/adapters/ponsim_onu/ponsim_onu.yml b/python/adapters/ponsim_onu/ponsim_onu.yml
similarity index 100%
rename from adapters/ponsim_onu/ponsim_onu.yml
rename to python/adapters/ponsim_onu/ponsim_onu.yml
diff --git a/adapters/protos/Makefile b/python/adapters/protos/Makefile
similarity index 100%
rename from adapters/protos/Makefile
rename to python/adapters/protos/Makefile
diff --git a/adapters/protos/__init__.py b/python/adapters/protos/__init__.py
similarity index 100%
rename from adapters/protos/__init__.py
rename to python/adapters/protos/__init__.py
diff --git a/adapters/protos/third_party/__init__.py b/python/adapters/protos/third_party/__init__.py
similarity index 100%
rename from adapters/protos/third_party/__init__.py
rename to python/adapters/protos/third_party/__init__.py
diff --git a/adapters/protos/third_party/google/LICENSE b/python/adapters/protos/third_party/google/LICENSE
similarity index 100%
rename from adapters/protos/third_party/google/LICENSE
rename to python/adapters/protos/third_party/google/LICENSE
diff --git a/adapters/protos/third_party/google/__init__.py b/python/adapters/protos/third_party/google/__init__.py
similarity index 100%
rename from adapters/protos/third_party/google/__init__.py
rename to python/adapters/protos/third_party/google/__init__.py
diff --git a/adapters/protos/third_party/google/api/__init__.py b/python/adapters/protos/third_party/google/api/__init__.py
similarity index 100%
rename from adapters/protos/third_party/google/api/__init__.py
rename to python/adapters/protos/third_party/google/api/__init__.py
diff --git a/adapters/protos/third_party/google/api/annotations.proto b/python/adapters/protos/third_party/google/api/annotations.proto
similarity index 100%
rename from adapters/protos/third_party/google/api/annotations.proto
rename to python/adapters/protos/third_party/google/api/annotations.proto
diff --git a/adapters/protos/third_party/google/api/http.proto b/python/adapters/protos/third_party/google/api/http.proto
similarity index 100%
rename from adapters/protos/third_party/google/api/http.proto
rename to python/adapters/protos/third_party/google/api/http.proto
diff --git a/adapters/requirements.txt b/python/adapters/requirements.txt
similarity index 100%
rename from adapters/requirements.txt
rename to python/adapters/requirements.txt
diff --git a/adapters/common/__init__.py b/python/common/__init__.py
similarity index 100%
copy from adapters/common/__init__.py
copy to python/common/__init__.py
diff --git a/adapters/common/event_bus.py b/python/common/event_bus.py
similarity index 100%
copy from adapters/common/event_bus.py
copy to python/common/event_bus.py
diff --git a/adapters/common/frameio/__init__.py b/python/common/frameio/__init__.py
similarity index 100%
copy from adapters/common/frameio/__init__.py
copy to python/common/frameio/__init__.py
diff --git a/python/common/frameio/frameio.py b/python/common/frameio/frameio.py
new file mode 100644
index 0000000..3f5bcf6
--- /dev/null
+++ b/python/common/frameio/frameio.py
@@ -0,0 +1,437 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+A module that can send and receive raw ethernet frames on a set of interfaces
+and it can manage a set of vlan interfaces on top of existing
+interfaces. Due to reliance on raw sockets, this module requires
+root access. Also, raw sockets are hard to deal with in Twisted (not
+directly supported) we need to run the receiver select loop on a dedicated
+thread.
+"""
+
+import os
+import socket
+import struct
+import uuid
+from pcapy import BPFProgram
+from threading import Thread, Condition
+
+import fcntl
+
+import select
+import structlog
+import sys
+
+from scapy.data import ETH_P_ALL
+from twisted.internet import reactor
+from zope.interface import implementer
+
+from voltha.registry import IComponent
+
+if sys.platform.startswith('linux'):
+    from common.frameio.third_party.oftest import afpacket, netutils
+elif sys.platform == 'darwin':
+    from scapy.arch import pcapdnet, BIOCIMMEDIATE, dnet
+
+log = structlog.get_logger()
+
+
+def hexify(buffer):
+    """
+    Return a hexadecimal string encoding of input buffer
+    """
+    return ''.join('%02x' % ord(c) for c in buffer)
+
+
+class _SelectWakerDescriptor(object):
+    """
+    A descriptor that can be mixed into a select loop to wake it up.
+    """
+    def __init__(self):
+        self.pipe_read, self.pipe_write = os.pipe()
+        fcntl.fcntl(self.pipe_write, fcntl.F_SETFL, os.O_NONBLOCK)
+
+    def __del__(self):
+        os.close(self.pipe_read)
+        os.close(self.pipe_write)
+
+    def fileno(self):
+        return self.pipe_read
+
+    def wait(self):
+        os.read(self.pipe_read, 1)
+
+    def notify(self):
+        """Trigger a select loop"""
+        os.write(self.pipe_write, '\x00')
+
+
+class BpfProgramFilter(object):
+    """
+    Convenience packet filter based on the well-tried Berkeley Packet Filter,
+    used by many well known open source tools such as pcap and tcpdump.
+    """
+    def __init__(self, program_string):
+        """
+        Create a filter using the BPF command syntax. To learn more,
+        consult 'man pcap-filter'.
+        :param program_string: The textual definition of the filter. Examples:
+        'vlan 1000'
+        'vlan 1000 and ip src host 10.10.10.10'
+        """
+        self.bpf = BPFProgram(program_string)
+
+    def __call__(self, frame):
+        """
+        Return 1 if frame passes filter.
+        :param frame: Raw frame provided as Python string
+        :return: 1 if frame satisfies filter, 0 otherwise.
+        """
+        return self.bpf.filter(frame)
+
+
+class FrameIOPort(object):
+    """
+    Represents a network interface which we can send/receive raw
+    Ethernet frames.
+    """
+
+    RCV_SIZE_DEFAULT = 4096
+    ETH_P_ALL = 0x03
+    RCV_TIMEOUT = 10000
+    MIN_PKT_SIZE = 60
+
+    def __init__(self, iface_name):
+        self.iface_name = iface_name
+        self.proxies = []
+        self.socket = self.open_socket(self.iface_name)
+        log.debug('socket-opened', fn=self.fileno(), iface=iface_name)
+        self.received = 0
+        self.discarded = 0
+
+    def add_proxy(self, proxy):
+        self.proxies.append(proxy)
+
+    def del_proxy(self, proxy):
+        self.proxies = [p for p in self.proxies if p.name != proxy.name]
+
+    def open_socket(self, iface_name):
+        raise NotImplementedError('to be implemented by derived class')
+
+    def rcv_frame(self):
+        raise NotImplementedError('to be implemented by derived class')
+
+    def __del__(self):
+        if self.socket:
+            self.socket.close()
+            self.socket = None
+        log.debug('socket-closed', iface=self.iface_name)
+
+    def fileno(self):
+        return self.socket.fileno()
+
+    def _dispatch(self, proxy, frame):
+        log.debug('calling-publisher', proxy=proxy.name, frame=hexify(frame))
+        try:
+            proxy.callback(proxy, frame)
+        except Exception as e:
+            log.exception('callback-error',
+                          explanation='Callback failed while processing frame',
+                          e=e)
+
+    def recv(self):
+        """Called on the select thread when a packet arrives"""
+        try:
+            frame = self.rcv_frame()
+        except RuntimeError as e:
+            # we observed this happens sometimes right after the socket was
+            # attached to a newly created veth interface. So we log it, but
+            # allow to continue.
+            log.warn('afpacket-recv-error', code=-1)
+            return
+
+        log.debug('frame-received', iface=self.iface_name, len=len(frame),
+                  hex=hexify(frame))
+        self.received +=1
+        dispatched = False
+        for proxy in self.proxies:
+            if proxy.filter is None or proxy.filter(frame):
+                log.debug('frame-dispatched')
+                dispatched = True
+                reactor.callFromThread(self._dispatch, proxy, frame)
+
+        if not dispatched:
+            self.discarded += 1
+            log.debug('frame-discarded')
+
+    def send(self, frame):
+        log.debug('sending', len=len(frame), iface=self.iface_name)
+        sent_bytes = self.send_frame(frame)
+        if sent_bytes != len(frame):
+            log.error('send-error', iface=self.iface_name,
+                      wanted_to_send=len(frame), actually_sent=sent_bytes)
+        return sent_bytes
+
+    def send_frame(self, frame):
+        try:
+            return self.socket.send(frame)
+        except socket.error, err:
+            if err[0] == os.errno.EINVAL:
+                if len(frame) < self.MIN_PKT_SIZE:
+                    padding = '\x00' * (self.MIN_PKT_SIZE - len(frame))
+                    frame = frame + padding
+                    return self.socket.send(frame)
+            else:
+                raise
+
+    def up(self):
+        if sys.platform.startswith('darwin'):
+            pass
+        else:
+            os.system('ip link set {} up'.format(self.iface_name))
+        return self
+
+    def down(self):
+        if sys.platform.startswith('darwin'):
+            pass
+        else:
+            os.system('ip link set {} down'.format(self.iface_name))
+        return self
+
+    def statistics(self):
+        return self.received, self.discarded
+
+
+class LinuxFrameIOPort(FrameIOPort):
+
+    def open_socket(self, iface_name):
+        s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, 0)
+        afpacket.enable_auxdata(s)
+        s.bind((self.iface_name, self.ETH_P_ALL))
+        netutils.set_promisc(s, iface_name)
+        s.settimeout(self.RCV_TIMEOUT)
+        return s
+
+    def rcv_frame(self):
+        return afpacket.recv(self.socket, self.RCV_SIZE_DEFAULT)
+
+
+class DarwinFrameIOPort(FrameIOPort):
+
+    def open_socket(self, iface_name):
+        sin = pcapdnet.open_pcap(iface_name, 1600, 1, 100)
+        try:
+            fcntl.ioctl(sin.fileno(), BIOCIMMEDIATE, struct.pack("I",1))
+        except:
+            pass
+
+        # need a different kind of socket for sending out
+        self.sout = dnet.eth(iface_name)
+
+        return sin
+
+    def send_frame(self, frame):
+        return self.sout.send(frame)
+
+    def rcv_frame(self):
+        pkt = self.socket.next()
+        if pkt is not None:
+            ts, pkt = pkt
+        return pkt
+
+
+if sys.platform == 'darwin':
+    _FrameIOPort = DarwinFrameIOPort
+elif sys.platform.startswith('linux'):
+    _FrameIOPort = LinuxFrameIOPort
+else:
+    raise Exception('Unsupported platform {}'.format(sys.platform))
+    sys.exit(1)
+
+
+class FrameIOPortProxy(object):
+    """Makes FrameIOPort sharable between multiple users"""
+
+    def __init__(self, frame_io_port, callback, filter=None, name=None):
+        self.frame_io_port = frame_io_port
+        self.callback = callback
+        self.filter = filter
+        self.name = uuid.uuid4().hex[:12] if name is None else name
+
+    @property
+    def iface_name(self):
+        return self.frame_io_port.iface_name
+
+    def get_iface_name(self):
+        return self.frame_io_port.iface_name
+
+    def send(self, frame):
+        return self.frame_io_port.send(frame)
+
+    def up(self):
+        self.frame_io_port.up()
+        return self
+
+    def down(self):
+        self.frame_io_port.down()
+        return self
+
+
+@implementer(IComponent)
+class FrameIOManager(Thread):
+    """
+    Packet/Frame IO manager that can be used to send/receive raw frames
+    on a set of network interfaces.
+    """
+    def __init__(self):
+        super(FrameIOManager, self).__init__()
+
+        self.ports = {}  # iface_name -> ActiveFrameReceiver
+        self.queue = {}  # iface_name -> TODO
+
+        self.cvar = Condition()
+        self.waker = _SelectWakerDescriptor()
+        self.stopped = False
+        self.ports_changed = False
+
+    # ~~~~~~~~~~~ exposed methods callable from main thread ~~~~~~~~~~~~~~~~~~~
+
+    def start(self):
+        """
+        Start the IO manager and its select loop thread
+        """
+        log.debug('starting')
+        super(FrameIOManager, self).start()
+        log.info('started')
+        return self
+
+    def stop(self):
+        """
+        Stop the IO manager and its thread with the select loop
+        """
+        log.debug('stopping')
+        self.stopped = True
+        self.waker.notify()
+        self.join()
+        del self.ports
+        log.info('stopped')
+
+    def list_interfaces(self):
+        """
+        Return list of interfaces listened on
+        :return: List of FrameIOPort objects
+        """
+        return self.ports
+
+    def open_port(self, iface_name, callback, filter=None, name=None):
+        """
+        Add a new interface and start receiving on it.
+        :param iface_name: Name of the interface. Must be an existing Unix
+        interface (eth0, en0, etc.)
+        :param callback: Called on each received frame;
+        signature: def callback(port, frame) where port is the FrameIOPort
+        instance at which the frame was received, frame is the actual frame
+        received (as binay string)
+        :param filter: An optional filter (predicate), with signature:
+        def filter(frame). If provided, only frames for which filter evaluates
+        to True will be forwarded to callback.
+        :return: FrmaeIOPortProxy instance.
+        """
+
+        port = self.ports.get(iface_name)
+        if port is None:
+            port = _FrameIOPort(iface_name)
+            self.ports[iface_name] = port
+            self.ports_changed = True
+            self.waker.notify()
+
+        proxy = FrameIOPortProxy(port, callback, filter, name)
+        port.add_proxy(proxy)
+
+        return proxy
+
+    def close_port(self, proxy):
+        """
+        Remove the proxy. If this is the last proxy on an interface, stop and
+        remove the named interface as well
+        :param proxy: FrameIOPortProxy reference
+        :return: None
+        """
+        assert isinstance(proxy, FrameIOPortProxy)
+        iface_name = proxy.get_iface_name()
+        assert iface_name in self.ports, "iface_name {} unknown".format(iface_name)
+        port = self.ports[iface_name]
+        port.del_proxy(proxy)
+
+        if not port.proxies:
+            del self.ports[iface_name]
+            # need to exit select loop to reconstruct select fd lists
+            self.ports_changed = True
+            self.waker.notify()
+
+    def send(self, iface_name, frame):
+        """
+        Send frame on given interface
+        :param iface_name: Name of previously registered interface
+        :param frame: frame as string
+        :return: number of bytes sent
+        """
+        return self.ports[iface_name].send(frame)
+
+    # ~~~~~~~~~~~~~ Thread methods (running on non-main thread ~~~~~~~~~~~~~~~~
+
+    def run(self):
+        """
+        Called on the alien thread, this is the core multi-port receive loop
+        """
+
+        log.debug('select-loop-started')
+
+        # outer loop constructs sockets list for select
+        while not self.stopped:
+            sockets = [self.waker] + self.ports.values()
+            self.ports_changed = False
+            empty = []
+            # inner select loop
+
+            while not self.stopped:
+                try:
+                    _in, _out, _err = select.select(sockets, empty, empty, 1)
+                except Exception as e:
+                    log.exception('frame-io-select-error', e=e)
+                    break
+                with self.cvar:
+                    for port in _in:
+                        if port is self.waker:
+                            self.waker.wait()
+                            continue
+                        else:
+                            port.recv()
+                    self.cvar.notify_all()
+                if self.ports_changed:
+                    break  # break inner loop so we reconstruct sockets list
+
+        log.debug('select-loop-exited')
+
+    def del_interface(self, iface_name):
+        """
+            Delete interface for stopping
+        """
+
+        log.info('Delete interface')
+        del self.ports[iface_name]
+        log.info('Interface(port) is deleted')
diff --git a/adapters/common/frameio/third_party/__init__.py b/python/common/frameio/third_party/__init__.py
similarity index 100%
copy from adapters/common/frameio/third_party/__init__.py
copy to python/common/frameio/third_party/__init__.py
diff --git a/adapters/common/frameio/third_party/oftest/LICENSE b/python/common/frameio/third_party/oftest/LICENSE
similarity index 100%
copy from adapters/common/frameio/third_party/oftest/LICENSE
copy to python/common/frameio/third_party/oftest/LICENSE
diff --git a/adapters/common/frameio/third_party/oftest/README.md b/python/common/frameio/third_party/oftest/README.md
similarity index 100%
copy from adapters/common/frameio/third_party/oftest/README.md
copy to python/common/frameio/third_party/oftest/README.md
diff --git a/adapters/common/frameio/third_party/oftest/__init__.py b/python/common/frameio/third_party/oftest/__init__.py
similarity index 100%
copy from adapters/common/frameio/third_party/oftest/__init__.py
copy to python/common/frameio/third_party/oftest/__init__.py
diff --git a/adapters/common/frameio/third_party/oftest/afpacket.py b/python/common/frameio/third_party/oftest/afpacket.py
similarity index 100%
copy from adapters/common/frameio/third_party/oftest/afpacket.py
copy to python/common/frameio/third_party/oftest/afpacket.py
diff --git a/adapters/common/frameio/third_party/oftest/netutils.py b/python/common/frameio/third_party/oftest/netutils.py
similarity index 100%
copy from adapters/common/frameio/third_party/oftest/netutils.py
copy to python/common/frameio/third_party/oftest/netutils.py
diff --git a/adapters/ponsim_olt/__init__.py b/python/common/kvstore/__init__.py
similarity index 100%
copy from adapters/ponsim_olt/__init__.py
copy to python/common/kvstore/__init__.py
diff --git a/python/common/kvstore/consul_client.py b/python/common/kvstore/consul_client.py
new file mode 100644
index 0000000..bc14759
--- /dev/null
+++ b/python/common/kvstore/consul_client.py
@@ -0,0 +1,304 @@
+# Copyright 2018-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.kvstore.kv_client import DEFAULT_TIMEOUT, Event, KVClient, KVPair, RETRY_BACKOFF
+from common.utils.asleep import asleep
+from common.utils.deferred_utils import DeferredWithTimeout, TimeOutError
+from consul import ConsulException
+from consul.twisted import Consul
+from structlog import get_logger
+from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
+
+log = get_logger()
+
+class ConsulClient(KVClient):
+
+    def __init__(self, kv_host, kv_port):
+        KVClient.__init__(self, kv_host, kv_port)
+        self.session_id = None
+        self.client = Consul(kv_host, kv_port)
+
+    def watch(self, key, key_change_callback, timeout=DEFAULT_TIMEOUT):
+        self._retriggering_watch(key, key_change_callback, timeout)
+
+    @inlineCallbacks
+    def _retriggering_watch(self, key, key_change_callback, timeout):
+        self.key_watches[key] = ConsulWatch(self.client, key, key_change_callback, timeout)
+        yield self.key_watches[key].start()
+
+    def close_watch(self, key, timeout=DEFAULT_TIMEOUT):
+        if key in self.key_watches:
+            self.key_watches[key].stop()
+
+    @inlineCallbacks
+    def _op_with_retry(self, operation, key, value, timeout, *args, **kw):
+        log.debug('kv-op', operation=operation, key=key, timeout=timeout, args=args, kw=kw)
+        err = None
+        result = None
+        while True:
+            try:
+                if operation == 'GET':
+                    result = yield self._get(key, **kw)
+                elif operation == 'LIST':
+                    result, err = yield self._list(key)
+                elif operation == 'PUT':
+                    # Put returns a boolean response
+                    result = yield self.client.kv.put(key, value)
+                    if not result:
+                        err = 'put-failed'
+                elif operation == 'DELETE':
+                    # Delete returns a boolean response
+                    result = yield self.client.kv.delete(key)
+                    if not result:
+                        err = 'delete-failed'
+                elif operation == 'RESERVE':
+                    result, err = yield self._reserve(key, value, **kw)
+                elif operation == 'RENEW':
+                    result, err = yield self._renew_reservation(key)
+                elif operation == 'RELEASE':
+                    result, err = yield self._release_reservation(key)
+                elif operation == 'RELEASE-ALL':
+                    err = yield self._release_all_reservations()
+                self._clear_backoff()
+                break
+            except ConsulException as ex:
+                if 'ConnectionRefusedError' in ex.message:
+                    log.exception('comms-exception', ex=ex)
+                    yield self._backoff('consul-not-up')
+                else:
+                    log.error('consul-specific-exception', ex=ex)
+                    err = ex
+            except Exception as ex:
+                log.error('consul-exception', ex=ex)
+                err = ex
+
+            if timeout > 0 and self.retry_time > timeout:
+                err = 'operation-timed-out'
+            if err is not None:
+                self._clear_backoff()
+                break
+
+        returnValue((result,err))
+
+    @inlineCallbacks
+    def _get(self, key, **kw):
+        kvp = None
+        index, rec = yield self.client.kv.get(key, **kw)
+        if rec is not None:
+            kvp = KVPair(rec['Key'], rec['Value'], index)
+        returnValue(kvp)
+
+    @inlineCallbacks
+    def _list(self, key):
+        err = None
+        list = []
+        index, recs = yield self.client.kv.get(key, recurse=True)
+        for rec in recs:
+            list.append(KVPair(rec['Key'], rec['Value'], rec['ModifyIndex']))
+        returnValue((list, err))
+
+    @inlineCallbacks
+    def _reserve(self, key, value, **kw):
+        for name, val in kw.items():
+            if name == 'ttl':
+                ttl = val
+                break
+        reserved = False
+        err = 'reservation-failed'
+        owner = None
+
+        # Create a session
+        self.session_id = yield self.client.session.create(behavior='delete',
+                                                           ttl=ttl) # lock_delay=1)
+        log.debug('create-session', id=self.session_id)
+        # Try to acquire the key
+        result = yield self.client.kv.put(key, value, acquire=self.session_id)
+        log.debug('key-acquire', key=key, value=value, sess=self.session_id, result=result)
+
+        # Check if reservation succeeded
+        index, record = yield self.client.kv.get(key)
+        if record is not None and 'Value' in record:
+            owner = record['Value']
+            log.debug('get-key', session=record['Session'], owner=owner)
+            if record['Session'] == self.session_id and owner == value:
+                reserved = True
+                log.debug('key-reserved', key=key, value=value, ttl=ttl)
+                # Add key to reservation list
+                self.key_reservations[key] = self.session_id
+            else:
+                log.debug('reservation-held-by-another', owner=owner)
+
+        if reserved:
+            err = None
+        returnValue((owner, err))
+
+    @inlineCallbacks
+    def _renew_reservation(self, key):
+        result = None
+        err = None
+        if key not in self.key_reservations:
+            err = 'key-not-reserved'
+        else:
+            session_id = self.key_reservations[key]
+            # A successfully renewed session returns an object with fields:
+            # Node, CreateIndex, Name, ModifyIndex, ID, Behavior, TTL,
+            # LockDelay, and Checks
+            result = yield self.client.session.renew(session_id=session_id)
+            log.debug('session-renew', result=result)
+        if result is None:
+            err = 'session-renewal-failed'
+        returnValue((result, err))
+
+    @inlineCallbacks
+    def _release_reservation(self, key):
+        err = None
+        if key not in self.key_reservations:
+            err = 'key-not-reserved'
+        else:
+            session_id = self.key_reservations[key]
+            # A successfully destroyed session returns a boolean result
+            success = yield self.client.session.destroy(session_id)
+            log.debug('session-destroy', result=success)
+            if not success:
+                err = 'session-destroy-failed'
+            self.session_id = None
+            self.key_reservations.pop(key)
+        returnValue((success, err))
+
+    @inlineCallbacks
+    def _release_all_reservations(self):
+        err = None
+        keys_to_delete = []
+        for key in self.key_reservations:
+            session_id = self.key_reservations[key]
+            # A successfully destroyed session returns a boolean result
+            success = yield self.client.session.destroy(session_id)
+            if not success:
+                err = 'session-destroy-failed'
+                log.debug('session-destroy', id=session_id, result=success)
+            self.session_id = None
+            keys_to_delete.append(key)
+        for key in keys_to_delete:
+            self.key_reservations.pop(key)
+        returnValue(err)
+
+
+class ConsulWatch():
+
+    def __init__(self, consul, key, callback, timeout):
+        self.client = consul
+        self.key = key
+        self.index = None
+        self.callback = callback
+        self.timeout = timeout
+        self.period = 60
+        self.running = True
+        self.retries = 0
+        self.retry_time = 0
+
+    @inlineCallbacks
+    def start(self):
+        self.running = True
+        index, rec = yield self._get_with_retry(self.key, None,
+                                              timeout=self.timeout)
+        self.index = str(index)
+
+        @inlineCallbacks
+        def _get(key, deferred):
+            try:
+                index, rec = yield self._get_with_retry(key, None,
+                                                     timeout=self.timeout,
+                                                     index=self.index)
+                self.index = str(index)
+                if not deferred.called:
+                    log.debug('got-result-cancelling-deferred')
+                    deferred.callback((self.index, rec))
+            except Exception as e:
+                log.exception('got-exception', e=e)
+
+        while self.running:
+            try:
+                rcvd = DeferredWithTimeout(timeout=self.period)
+                _get(self.key, rcvd)
+                try:
+                    # Update index for next watch iteration
+                    index, rec = yield rcvd
+                    log.debug('event-received', index=index, rec=rec)
+                    # Notify client of key change event
+                    if rec is None:
+                        # Key has been deleted
+                        self._send_event(Event(Event.DELETE, self.key, None))
+                    else:
+                        self._send_event(Event(Event.PUT, rec['Key'], rec['Value']))
+                except TimeOutError as e:
+                    log.debug('no-events-over-watch-period', key=self.key)
+                except Exception as e:
+                    log.exception('exception', e=e)
+            except Exception as e:
+                log.exception('exception', e=e)
+
+        log.debug('close-watch', key=self.key)
+
+    def stop(self):
+        self.running = False
+        self.callback = None
+
+    @inlineCallbacks
+    def _get_with_retry(self, key, value, timeout, *args, **kw):
+        log.debug('watch-period', key=key, period=self.period, timeout=timeout, args=args, kw=kw)
+        err = None
+        result = None
+        while True:
+            try:
+                result = yield self.client.kv.get(key, **kw)
+                self._clear_backoff()
+                break
+            except ConsulException as ex:
+                err = ex
+                if 'ConnectionRefusedError' in ex.message:
+                    self._send_event(Event(Event.CONNECTION_DOWN, self.key, None))
+                    log.exception('comms-exception', ex=ex)
+                    yield self._backoff('consul-not-up')
+                else:
+                    log.error('consul-specific-exception', ex=ex)
+            except Exception as ex:
+                err = ex
+                log.error('consul-exception', ex=ex)
+
+            if timeout > 0 and self.retry_time > timeout:
+                err = 'operation-timed-out'
+            if err is not None:
+                self._clear_backoff()
+                break
+
+        returnValue(result)
+
+    def _send_event(self, event):
+        if self.callback is not None:
+            self.callback(event)
+
+    def _backoff(self, msg):
+        wait_time = RETRY_BACKOFF[min(self.retries, len(RETRY_BACKOFF) - 1)]
+        self.retry_time += wait_time
+        self.retries += 1
+        log.error(msg, next_retry_in_secs=wait_time,
+                  total_delay_in_secs = self.retry_time,
+                  retries=self.retries)
+        return asleep(wait_time)
+
+    def _clear_backoff(self):
+        if self.retries:
+            log.debug('reconnected-to-kv', after_retries=self.retries)
+            self.retries = 0
+            self.retry_time = 0
diff --git a/python/common/kvstore/etcd_client.py b/python/common/kvstore/etcd_client.py
new file mode 100644
index 0000000..a958b71
--- /dev/null
+++ b/python/common/kvstore/etcd_client.py
@@ -0,0 +1,240 @@
+# Copyright 2018-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+################################################################################
+#
+# Most of the txaioetcd methods provide a timeout parameter. This parameter
+# is likely intended to limit the amount of time spent by any one method
+# waiting for a response from the etcd server. However, if the server is
+# down, the method immediately throws a ConnectionRefusedError exception;
+# it does not perform any retries. The timeout parameter provided by the
+# methods in EtcdClient cover this contingency.
+#
+################################################################################
+
+from common.kvstore.kv_client import DEFAULT_TIMEOUT, Event, KVClient, KVPair
+from structlog import get_logger
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
+from twisted.internet.error import ConnectionRefusedError
+from txaioetcd import Client, CompVersion, Failed, KeySet, OpGet, OpSet, Transaction
+
+log = get_logger()
+
+class EtcdClient(KVClient):
+
+    def __init__(self, kv_host, kv_port):
+        KVClient.__init__(self, kv_host, kv_port)
+        self.url = u'http://' + kv_host + u':' + str(kv_port)
+        self.client = Client(reactor, self.url)
+
+    @inlineCallbacks
+    def watch(self, key, key_change_callback, timeout=DEFAULT_TIMEOUT):
+        self.key_watches[key] = key_change_callback
+        result = yield self._op_with_retry('WATCH', key, None, timeout, callback=self.key_changed)
+        returnValue(result)
+
+    def key_changed(self, kv):
+        key = kv.key
+        value = kv.value
+        log.debug('key-changed', key=key, value=value)
+        # Notify client of key change event
+        if value is not None:
+            evt = Event(Event.PUT, key, value)
+        else:
+            evt = Event(Event.DELETE, key, None)
+        if key in self.key_watches:
+            self.key_watches[key](evt)
+
+    def close_watch(self, key, timeout=DEFAULT_TIMEOUT):
+        log.debug('close-watch', key=key)
+        if key in self.key_watches:
+            self.key_watches.pop(key)
+
+    @inlineCallbacks
+    def _op_with_retry(self, operation, key, value, timeout, *args, **kw):
+        log.debug('kv-op', operation=operation, key=key, timeout=timeout, args=args, kw=kw)
+        err = None
+        result = None
+        if type(key) == str:
+            key = bytes(key)
+        if value is not None:
+           value = bytes(value)
+        while True:
+            try:
+                if operation == 'GET':
+                    result = yield self._get(key)
+                elif operation == 'LIST':
+                    result, err = yield self._list(key)
+                elif operation == 'PUT':
+                    # Put returns an object of type Revision
+                    result = yield self.client.set(key, value, **kw)
+                elif operation == 'DELETE':
+                    # Delete returns an object of type Deleted
+                    result = yield self.client.delete(key)
+                elif operation == 'RESERVE':
+                    result, err = yield self._reserve(key, value, **kw)
+                elif operation == 'RENEW':
+                    result, err = yield self._renew_reservation(key)
+                elif operation == 'RELEASE':
+                    result, err = yield self._release_reservation(key)
+                elif operation == 'RELEASE-ALL':
+                    err = yield self._release_all_reservations()
+                elif operation == 'WATCH':
+                    for name, val in kw.items():
+                        if name == 'callback':
+                            callback = val
+                            break
+                    result = self.client.watch([KeySet(key, prefix=True)], callback)
+                self._clear_backoff()
+                break
+            except ConnectionRefusedError as ex:
+                log.error('comms-exception', ex=ex)
+                yield self._backoff('etcd-not-up')
+            except Exception as ex:
+                log.error('etcd-exception', ex=ex)
+                err = ex
+
+            if timeout > 0 and self.retry_time > timeout:
+                err = 'operation-timed-out'
+            if err is not None:
+                self._clear_backoff()
+                break
+
+        returnValue((result, err))
+
+    @inlineCallbacks
+    def _get(self, key):
+        kvp = None
+        resp = yield self.client.get(key)
+        if resp.kvs is not None and len(resp.kvs) == 1:
+            kv = resp.kvs[0]
+            kvp = KVPair(kv.key, kv.value, kv.mod_revision)
+        returnValue(kvp)
+
+    @inlineCallbacks
+    def _list(self, key):
+        err = None
+        list = []
+        resp = yield self.client.get(KeySet(key, prefix=True))
+        if resp.kvs is not None and len(resp.kvs) > 0:
+            for kv in resp.kvs:
+                list.append(KVPair(kv.key, kv.value, kv.mod_revision))
+        returnValue((list, err))
+
+    @inlineCallbacks
+    def _reserve(self, key, value, **kw):
+        for name, val in kw.items():
+            if name == 'ttl':
+                ttl = val
+                break
+        reserved = False
+        err = 'reservation-failed'
+        owner = None
+
+        # Create a lease
+        lease = yield self.client.lease(ttl)
+
+        # Create a transaction
+        txn = Transaction(
+            compare=[ CompVersion(key, '==', 0) ],
+            success=[ OpSet(key, bytes(value), lease=lease) ],
+            failure=[ OpGet(key) ]
+        )
+        newly_acquired = False
+        try:
+            result = yield self.client.submit(txn)
+        except Failed as failed:
+            log.debug('key-already-present', key=key)
+            if len(failed.responses) > 0:
+                response = failed.responses[0]
+                if response.kvs is not None and len(response.kvs) > 0:
+                    kv = response.kvs[0]
+                    log.debug('key-already-present', value=kv.value)
+                    if kv.value == value:
+                        reserved = True
+                        log.debug('key-already-reserved', key = kv.key, value=kv.value)
+        else:
+            newly_acquired = True
+            log.debug('key-was-absent', key=key, result=result)
+
+        # Check if reservation succeeded
+        resp = yield self.client.get(key)
+        if resp.kvs is not None and len(resp.kvs) == 1:
+            owner = resp.kvs[0].value
+            if owner == value:
+                if newly_acquired:
+                    log.debug('key-reserved', key=key, value=value, ttl=ttl,
+                             lease_id=lease.lease_id)
+                    reserved = True
+                    # Add key to reservation list
+                    self.key_reservations[key] = lease
+                else:
+                    log.debug("reservation-still-held")
+            else:
+                log.debug('reservation-held-by-another', value=owner)
+
+        if reserved:
+            err = None
+        returnValue((owner, err))
+
+    @inlineCallbacks
+    def _renew_reservation(self, key):
+        result = None
+        err = None
+        if key not in self.key_reservations:
+            err = 'key-not-reserved'
+        else:
+            lease = self.key_reservations[key]
+            # A successfully refreshed lease returns an object of type Header
+            result = yield lease.refresh()
+        if result is None:
+            err = 'lease-refresh-failed'
+        returnValue((result, err))
+
+    @inlineCallbacks
+    def _release_reservation(self, key):
+        err = None
+        if key not in self.key_reservations:
+            err = 'key-not-reserved'
+        else:
+            lease = self.key_reservations[key]
+            time_left = yield lease.remaining()
+            # A successfully revoked lease returns an object of type Header
+            log.debug('release-reservation', key=key, lease_id=lease.lease_id,
+                      time_left_in_secs=time_left)
+            result = yield lease.revoke()
+            if result is None:
+                err = 'lease-revoke-failed'
+            self.key_reservations.pop(key)
+        returnValue((result, err))
+
+    @inlineCallbacks
+    def _release_all_reservations(self):
+        err = None
+        keys_to_delete = []
+        for key in self.key_reservations:
+            lease = self.key_reservations[key]
+            time_left = yield lease.remaining()
+            # A successfully revoked lease returns an object of type Header
+            log.debug('release-reservation', key=key, lease_id=lease.lease_id,
+                      time_left_in_secs=time_left)
+            result = yield lease.revoke()
+            if result is None:
+                err = 'lease-revoke-failed'
+                log.debug('lease-revoke', result=result)
+            keys_to_delete.append(key)
+        for key in keys_to_delete:
+            self.key_reservations.pop(key)
+        returnValue(err)
diff --git a/python/common/kvstore/kv_client.py b/python/common/kvstore/kv_client.py
new file mode 100644
index 0000000..69a6480
--- /dev/null
+++ b/python/common/kvstore/kv_client.py
@@ -0,0 +1,206 @@
+# Copyright 2018-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.utils.asleep import asleep
+from structlog import get_logger
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+log = get_logger()
+
+class KVPair():
+    def __init__(self, key, value, index):
+        self.key = key
+        self.value = value
+        self.index = index
+
+class Event():
+    PUT = 0
+    DELETE = 1
+    CONNECTION_DOWN = 2
+
+    def __init__(self, event_type, key, value):
+        self.event_type = event_type
+        self.key = key
+        self.value = value
+
+RETRY_BACKOFF = [0.05, 0.1, 0.2, 0.5, 1, 2, 5]
+DEFAULT_TIMEOUT = 0.0
+for i in range(len(RETRY_BACKOFF)):
+    DEFAULT_TIMEOUT += RETRY_BACKOFF[i]
+
+class KVClient():
+
+    def __init__(self, kv_host, kv_port):
+        self.host = kv_host
+        self.port = kv_port
+        self.key_reservations = {}
+        self.key_watches = {}
+        self.retries = 0
+        self.retry_time = 0
+
+    @inlineCallbacks
+    def get(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method returns the value of the given key in KV store.
+
+        :param key: The key whose value is requested
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: (KVPair, error) where KVPair is None if an error occurred
+        '''
+        result = yield self._op_with_retry('GET', key, None, timeout)
+        returnValue(result)
+
+    @inlineCallbacks
+    def list(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        The list method returns an array of key-value pairs all of which
+        share the same key prefix.
+
+        :param key: The key prefix
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: ([]KVPair, error) where []KVPair is a list of KVPair objects
+        '''
+        result = yield self._op_with_retry('LIST', key, None, timeout)
+        returnValue(result)
+
+    @inlineCallbacks
+    def put(self, key, value, timeout=DEFAULT_TIMEOUT):
+        '''
+        The put method writes a value to the given key in KV store.
+        Do NOT modify a reserved key in an etcd store; doing so seems
+        to nullify the TTL of the key. In other words, the key lasts
+        forever.
+
+        :param key: The key to be written to
+        :param value: The value of the key
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: error, which is set to None for a successful write
+        '''
+        _, err = yield self._op_with_retry('PUT', key, value, timeout)
+        returnValue(err)
+
+    @inlineCallbacks
+    def delete(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        The delete method removes a key from the KV store.
+
+        :param key: The key to be deleted
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: error, which is set to None for a successful deletion
+        '''
+        _, err = yield self._op_with_retry('DELETE', key, None, timeout)
+        returnValue(err)
+
+    @inlineCallbacks
+    def reserve(self, key, value, ttl, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method acts essentially like a semaphore. The underlying mechanism
+        differs depending on the KV store: etcd uses a test-and-set transaction;
+        consul uses an acquire lock. If using etcd, do NOT write to the key
+        subsequent to the initial reservation; the TTL functionality may become
+        impaired (i.e. the reservation never expires).
+
+        :param key: The key under reservation
+        :param value: The reservation owner
+        :param ttl: The time-to-live (TTL) for the reservation. The key is unreserved
+        by the KV store when the TTL expires.
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: (key_value, error) If the key is acquired, then the value returned will
+        be the value passed in.  If the key is already acquired, then the value assigned
+        to that key will be returned.
+        '''
+        result = yield self._op_with_retry('RESERVE', key, value, timeout, ttl=ttl)
+        returnValue(result)
+
+    @inlineCallbacks
+    def renew_reservation(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method renews the reservation for a given key. A reservation expires
+        after the TTL (Time To Live) period specified when reserving the key.
+
+        :param key: The reserved key
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: error, which is set to None for a successful renewal
+        '''
+        result, err = yield self._op_with_retry('RENEW', key, None, timeout)
+        returnValue(err)
+
+    @inlineCallbacks
+    def release_reservation(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        The release_reservation method cancels the reservation for a given key.
+
+        :param key: The reserved key
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: error, which is set to None for a successful cancellation
+        '''
+        result, err = yield self._op_with_retry('RELEASE', key, None, timeout)
+        returnValue(err)
+
+    @inlineCallbacks
+    def release_all_reservations(self, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method cancels all key reservations made previously
+        using the reserve API.
+
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: error, which is set to None for a successful cancellation
+        '''
+        result, err = yield self._op_with_retry('RELEASE-ALL', None, None, timeout)
+        returnValue(err)
+
+    @inlineCallbacks
+    def watch(self, key, key_change_callback, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method provides a watch capability for the given key. If the value of the key
+        changes or the key is deleted, then an event indicating the change is passed to
+        the given callback function.
+
+        :param key: The key to be watched
+        :param key_change_callback: The function invoked whenever the key changes
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: There is no return; key change events are passed to the callback function
+        '''
+        raise NotImplementedError('Method not implemented')
+
+    @inlineCallbacks
+    def close_watch(self, key, timeout=DEFAULT_TIMEOUT):
+        '''
+        This method closes the watch on the given key. Once the watch is closed, key
+        change events are no longer passed to the key change callback function.
+
+        :param key: The key under watch
+        :param timeout: The length of time in seconds the method will wait for a response
+        :return: There is no return
+        '''
+        raise NotImplementedError('Method not implemented')
+
+    @inlineCallbacks
+    def _op_with_retry(self, operation, key, value, timeout, *args, **kw):
+        raise NotImplementedError('Method not implemented')
+
+    def _backoff(self, msg):
+        wait_time = RETRY_BACKOFF[min(self.retries, len(RETRY_BACKOFF) - 1)]
+        self.retry_time += wait_time
+        self.retries += 1
+        log.error(msg, next_retry_in_secs=wait_time,
+                  total_delay_in_secs = self.retry_time,
+                  retries=self.retries)
+        return asleep(wait_time)
+
+    def _clear_backoff(self):
+        if self.retries:
+            log.debug('reset-backoff', after_retries=self.retries)
+            self.retries = 0
+            self.retry_time = 0
\ No newline at end of file
diff --git a/python/common/kvstore/kvstore.py b/python/common/kvstore/kvstore.py
new file mode 100644
index 0000000..662b34d
--- /dev/null
+++ b/python/common/kvstore/kvstore.py
@@ -0,0 +1,31 @@
+# Copyright 2018-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.kvstore.consul_client import ConsulClient
+from common.kvstore.etcd_client import EtcdClient
+
+def create_kv_client(kv_store, host, port):
+    '''
+    Factory for creating a client interface to a KV store
+
+    :param kv_store: Specify either 'etcd' or 'consul'
+    :param host: Name or IP address of host serving the KV store
+    :param port: Port number (integer) of the KV service
+    :return: Reference to newly created client interface
+    '''
+    if kv_store == 'etcd':
+        return EtcdClient(host, port)
+    elif kv_store == 'consul':
+        return ConsulClient(host, port)
+    return None
diff --git a/adapters/common/manhole.py b/python/common/manhole.py
similarity index 100%
copy from adapters/common/manhole.py
copy to python/common/manhole.py
diff --git a/python/common/pon_resource_manager/__init__.py b/python/common/pon_resource_manager/__init__.py
new file mode 100644
index 0000000..2d104e0
--- /dev/null
+++ b/python/common/pon_resource_manager/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+# http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

diff --git a/python/common/pon_resource_manager/resource_kv_store.py b/python/common/pon_resource_manager/resource_kv_store.py
new file mode 100644
index 0000000..a1a5c14
--- /dev/null
+++ b/python/common/pon_resource_manager/resource_kv_store.py
@@ -0,0 +1,107 @@
+#

+# Copyright 2018 the original author or authors.

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+# http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+

+"""Resource KV store - interface between Resource Manager and backend store."""

+import structlog

+

+from voltha.core.config.config_backend import ConsulStore

+from voltha.core.config.config_backend import EtcdStore

+

+# KV store uses this prefix to store resource info

+PATH_PREFIX = 'resource_manager/{}'

+

+

+class ResourceKvStore(object):

+    """Implements apis to store/get/remove resource in backend store."""

+

+    def __init__(self, technology, device_id, backend, host, port):

+        """

+        Create ResourceKvStore object.

+

+        Based on backend ('consul' and 'etcd' use the host and port

+        to create the respective object.

+

+        :param technology: PON technology

+        :param device_id: OLT device id

+        :param backend: Type of backend storage (etcd or consul)

+        :param host: host ip info for backend storage

+        :param port: port for the backend storage

+        :raises exception when invalid backend store passed as an argument

+        """

+        # logger

+        self._log = structlog.get_logger()

+

+        path = PATH_PREFIX.format(technology)

+        try:

+            if backend == 'consul':

+                self._kv_store = ConsulStore(host, port, path)

+            elif backend == 'etcd':

+                self._kv_store = EtcdStore(host, port, path)

+            else:

+                self._log.error('Invalid-backend')

+                raise Exception("Invalid-backend-for-kv-store")

+        except Exception as e:

+            self._log.exception("exception-in-init")

+            raise Exception(e)

+

+    def update_to_kv_store(self, path, resource):

+        """

+        Update resource.

+

+        :param path: path to update the resource

+        :param resource: updated resource

+        """

+        try:

+            self._kv_store[path] = str(resource)

+            self._log.debug("Resource-updated-in-kv-store", path=path)

+            return True

+        except BaseException:

+            self._log.exception("Resource-update-in-kv-store-failed",

+                                path=path, resource=resource)

+        return False

+

+    def get_from_kv_store(self, path):

+        """

+        Get resource.

+

+        :param path: path to get the resource

+        """

+        resource = None

+        try:

+            resource = self._kv_store[path]

+            self._log.debug("Got-resource-from-kv-store", path=path)

+        except KeyError:

+            self._log.info("Resource-not-found-updating-resource",

+                           path=path)

+        except BaseException:

+            self._log.exception("Getting-resource-from-kv-store-failed",

+                                path=path)

+        return resource

+

+    def remove_from_kv_store(self, path):

+        """

+        Remove resource.

+

+        :param path: path to remove the resource

+        """

+        try:

+            del self._kv_store[path]

+            self._log.debug("Resource-deleted-in-kv-store", path=path)

+            return True

+        except BaseException:

+            self._log.exception("Resource-delete-in-kv-store-failed",

+                                path=path)

+        return False

diff --git a/python/common/pon_resource_manager/resource_manager.py b/python/common/pon_resource_manager/resource_manager.py
new file mode 100644
index 0000000..17b2871
--- /dev/null
+++ b/python/common/pon_resource_manager/resource_manager.py
@@ -0,0 +1,677 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Resource Manager will be unique for each OLT device.
+
+It exposes APIs to create/free alloc_ids/onu_ids/gemport_ids. Resource Manager
+uses a KV store in backend to ensure resiliency of the data.
+"""
+import json
+import structlog
+from bitstring import BitArray
+from ast import literal_eval
+import shlex
+from argparse import ArgumentParser, ArgumentError
+
+from common.pon_resource_manager.resource_kv_store import ResourceKvStore
+
+
+# Used to parse extra arguments to OpenOlt adapter from the NBI
+class OltVendorArgumentParser(ArgumentParser):
+    # Must override the exit command to prevent it from
+    # calling sys.exit().  Return exception instead.
+    def exit(self, status=0, message=None):
+        raise Exception(message)
+
+
+class PONResourceManager(object):
+    """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
+
+    # Constants to identify resource pool
+    ONU_ID = 'ONU_ID'
+    ALLOC_ID = 'ALLOC_ID'
+    GEMPORT_ID = 'GEMPORT_ID'
+
+    # The resource ranges for a given device vendor_type should be placed
+    # at 'resource_manager/<technology>/resource_ranges/<olt_vendor_type>'
+    # path on the KV store.
+    # If Resource Range parameters are to be read from the external KV store,
+    # they are expected to be stored in the following format.
+    # Note: All parameters are MANDATORY for now.
+    '''
+    {
+        "onu_id_start": 1,
+        "onu_id_end": 127,
+        "alloc_id_start": 1024,
+        "alloc_id_end": 2816,
+        "gemport_id_start": 1024,
+        "gemport_id_end": 8960,
+        "pon_ports": 16
+    }
+
+    '''
+    # constants used as keys to reference the resource range parameters from
+    # and external KV store.
+    ONU_START_IDX = "onu_id_start"
+    ONU_END_IDX = "onu_id_end"
+    ALLOC_ID_START_IDX = "alloc_id_start"
+    ALLOC_ID_END_IDX = "alloc_id_end"
+    GEM_PORT_ID_START_IDX = "gemport_id_start"
+    GEM_PORT_ID_END_IDX = "gemport_id_end"
+    NUM_OF_PON_PORT = "pon_ports"
+
+    # PON Resource range configuration on the KV store.
+    # Format: 'resource_manager/<technology>/resource_ranges/<olt_vendor_type>'
+    # The KV store backend is initialized with a path prefix and we need to
+    # provide only the suffix.
+    PON_RESOURCE_RANGE_CONFIG_PATH = 'resource_ranges/{}'
+
+    # resource path suffix
+    ALLOC_ID_POOL_PATH = '{}/alloc_id_pool/{}'
+    GEMPORT_ID_POOL_PATH = '{}/gemport_id_pool/{}'
+    ONU_ID_POOL_PATH = '{}/onu_id_pool/{}'
+
+    # Path on the KV store for storing list of alloc IDs for a given ONU
+    # Format: <device_id>/<(pon_intf_id, onu_id)>/alloc_ids
+    ALLOC_ID_RESOURCE_MAP_PATH = '{}/{}/alloc_ids'
+
+    # Path on the KV store for storing list of gemport IDs for a given ONU
+    # Format: <device_id>/<(pon_intf_id, onu_id)>/gemport_ids
+    GEMPORT_ID_RESOURCE_MAP_PATH = '{}/{}/gemport_ids'
+
+    # Constants for internal usage.
+    PON_INTF_ID = 'pon_intf_id'
+    START_IDX = 'start_idx'
+    END_IDX = 'end_idx'
+    POOL = 'pool'
+
+    def __init__(self, technology, extra_args, device_id,
+                 backend, host, port):
+        """
+        Create PONResourceManager object.
+
+        :param technology: PON technology
+        :param: extra_args: This string contains extra arguments passed during
+        pre-provisioning of OLT and specifies the OLT Vendor type
+        :param device_id: OLT device id
+        :param backend: backend store
+        :param host: ip of backend store
+        :param port: port on which backend store listens
+        :raises exception when invalid backend store passed as an argument
+        """
+        # logger
+        self._log = structlog.get_logger()
+
+        try:
+            self.technology = technology
+            self.extra_args = extra_args
+            self.device_id = device_id
+            self.backend = backend
+            self.host = host
+            self.port = port
+            self.olt_vendor = None
+            self._kv_store = ResourceKvStore(technology, device_id, backend,
+                                             host, port)
+            # Below attribute, pon_resource_ranges, should be initialized
+            # by reading from KV store.
+            self.pon_resource_ranges = dict()
+        except Exception as e:
+            self._log.exception("exception-in-init")
+            raise Exception(e)
+
+    def init_resource_ranges_from_kv_store(self):
+        """
+        Initialize PON resource ranges with config fetched from kv store.
+
+        :return boolean: True if PON resource ranges initialized else false
+        """
+        self.olt_vendor = self._get_olt_vendor()
+        # Try to initialize the PON Resource Ranges from KV store based on the
+        # OLT vendor key, if available
+        if self.olt_vendor is None:
+            self._log.info("olt-vendor-unavailable--not-reading-from-kv-store")
+            return False
+
+        path = self.PON_RESOURCE_RANGE_CONFIG_PATH.format(self.olt_vendor)
+        try:
+            # get resource from kv store
+            result = self._kv_store.get_from_kv_store(path)
+
+            if result is None:
+                self._log.debug("resource-range-config-unavailable-on-kvstore")
+                return False
+
+            resource_range_config = result
+
+            if resource_range_config is not None:
+                self.pon_resource_ranges = json.loads(resource_range_config)
+                self._log.debug("Init-resource-ranges-from-kvstore-success",
+                                pon_resource_ranges=self.pon_resource_ranges,
+                                path=path)
+                return True
+
+        except Exception as e:
+            self._log.exception("error-initializing-resource-range-from-kv-store",
+                                e=e)
+        return False
+
+    def init_default_pon_resource_ranges(self, onu_start_idx=1,
+                                         onu_end_idx=127,
+                                         alloc_id_start_idx=1024,
+                                         alloc_id_end_idx=2816,
+                                         gem_port_id_start_idx=1024,
+                                         gem_port_id_end_idx=8960,
+                                         num_of_pon_ports=16):
+        """
+        Initialize default PON resource ranges
+
+        :param onu_start_idx: onu id start index
+        :param onu_end_idx: onu id end index
+        :param alloc_id_start_idx: alloc id start index
+        :param alloc_id_end_idx: alloc id end index
+        :param gem_port_id_start_idx: gemport id start index
+        :param gem_port_id_end_idx: gemport id end index
+        :param num_of_pon_ports: number of PON ports
+        """
+        self._log.info("initialize-default-resource-range-values")
+        self.pon_resource_ranges[
+            PONResourceManager.ONU_START_IDX] = onu_start_idx
+        self.pon_resource_ranges[PONResourceManager.ONU_END_IDX] = onu_end_idx
+        self.pon_resource_ranges[
+            PONResourceManager.ALLOC_ID_START_IDX] = alloc_id_start_idx
+        self.pon_resource_ranges[
+            PONResourceManager.ALLOC_ID_END_IDX] = alloc_id_end_idx
+        self.pon_resource_ranges[
+            PONResourceManager.GEM_PORT_ID_START_IDX] = gem_port_id_start_idx
+        self.pon_resource_ranges[
+            PONResourceManager.GEM_PORT_ID_END_IDX] = gem_port_id_end_idx
+        self.pon_resource_ranges[
+            PONResourceManager.NUM_OF_PON_PORT] = num_of_pon_ports
+
+    def init_device_resource_pool(self):
+        """
+        Initialize resource pool for all PON ports.
+        """
+        i = 0
+        while i < self.pon_resource_ranges[PONResourceManager.NUM_OF_PON_PORT]:
+            self.init_resource_id_pool(
+                pon_intf_id=i,
+                resource_type=PONResourceManager.ONU_ID,
+                start_idx=self.pon_resource_ranges[
+                    PONResourceManager.ONU_START_IDX],
+                end_idx=self.pon_resource_ranges[
+                    PONResourceManager.ONU_END_IDX])
+
+            i += 1
+
+        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
+        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
+        # for both the resource types. This may need to change later.
+        self.init_resource_id_pool(
+            pon_intf_id=0,
+            resource_type=PONResourceManager.ALLOC_ID,
+            start_idx=self.pon_resource_ranges[
+                PONResourceManager.ALLOC_ID_START_IDX],
+            end_idx=self.pon_resource_ranges[
+                PONResourceManager.ALLOC_ID_END_IDX])
+
+        self.init_resource_id_pool(
+            pon_intf_id=0,
+            resource_type=PONResourceManager.GEMPORT_ID,
+            start_idx=self.pon_resource_ranges[
+                PONResourceManager.GEM_PORT_ID_START_IDX],
+            end_idx=self.pon_resource_ranges[
+                PONResourceManager.GEM_PORT_ID_END_IDX])
+
+    def clear_device_resource_pool(self):
+        """
+        Clear resource pool of all PON ports.
+        """
+        i = 0
+        while i < self.pon_resource_ranges[PONResourceManager.NUM_OF_PON_PORT]:
+            self.clear_resource_id_pool(
+                pon_intf_id=i,
+                resource_type=PONResourceManager.ONU_ID,
+            )
+            i += 1
+
+        self.clear_resource_id_pool(
+            pon_intf_id=0,
+            resource_type=PONResourceManager.ALLOC_ID,
+        )
+
+        self.clear_resource_id_pool(
+            pon_intf_id=0,
+            resource_type=PONResourceManager.GEMPORT_ID,
+        )
+
+    def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx,
+                              end_idx):
+        """
+        Initialize Resource ID pool for a given Resource Type on a given PON Port
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param start_idx: start index for onu id pool
+        :param end_idx: end index for onu id pool
+        :return boolean: True if resource id pool initialized else false
+        """
+        status = False
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            # In case of adapter reboot and reconciliation resource in kv store
+            # checked for its presence if not kv store update happens
+            resource = self._get_resource(path)
+
+            if resource is not None:
+                self._log.info("Resource-already-present-in-store", path=path)
+                status = True
+            else:
+                resource = self._format_resource(pon_intf_id, start_idx,
+                                                 end_idx)
+                self._log.info("Resource-initialized", path=path)
+
+                # Add resource as json in kv store.
+                result = self._kv_store.update_to_kv_store(path, resource)
+                if result is True:
+                    status = True
+
+        except Exception as e:
+            self._log.exception("error-initializing-resource-pool", e=e)
+
+        return status
+
+    def get_resource_id(self, pon_intf_id, resource_type, num_of_id=1):
+        """
+        Create alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param num_of_id: required number of ids
+        :return list/int/None: list, int or None if resource type is
+                               alloc_id/gemport_id, onu_id or invalid type
+                               respectively
+        """
+        result = None
+
+        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
+        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
+        # for both the resource types. This may need to change later.
+        # Override the incoming pon_intf_id to PON0
+        if resource_type == PONResourceManager.GEMPORT_ID or \
+                resource_type == PONResourceManager.ALLOC_ID:
+            pon_intf_id = 0
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return result
+
+        try:
+            resource = self._get_resource(path)
+            if resource is not None and resource_type == \
+                    PONResourceManager.ONU_ID:
+                result = self._generate_next_id(resource)
+            elif resource is not None and (
+                    resource_type == PONResourceManager.GEMPORT_ID or
+                    resource_type == PONResourceManager.ALLOC_ID):
+                result = list()
+                while num_of_id > 0:
+                    result.append(self._generate_next_id(resource))
+                    num_of_id -= 1
+            else:
+                raise Exception("get-resource-failed")
+
+            self._log.debug("Get-" + resource_type + "-success", result=result,
+                            path=path)
+            # Update resource in kv store
+            self._update_resource(path, resource)
+
+        except Exception as e:
+            self._log.exception("Get-" + resource_type + "-id-failed",
+                                path=path, e=e)
+        return result
+
+    def free_resource_id(self, pon_intf_id, resource_type, release_content):
+        """
+        Release alloc/gemport/onu id for given OLT PON interface.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :param release_content: required number of ids
+        :return boolean: True if all IDs in given release_content released
+                         else False
+        """
+        status = False
+
+        # TODO: ASFvOLT16 platform requires alloc and gemport ID to be unique
+        # across OLT. To keep it simple, a single pool (POOL 0) is maintained
+        # for both the resource types. This may need to change later.
+        # Override the incoming pon_intf_id to PON0
+        if resource_type == PONResourceManager.GEMPORT_ID or \
+                resource_type == PONResourceManager.ALLOC_ID:
+            pon_intf_id = 0
+
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return status
+
+        try:
+            resource = self._get_resource(path)
+            if resource is not None and resource_type == \
+                    PONResourceManager.ONU_ID:
+                self._release_id(resource, release_content)
+            elif resource is not None and (
+                    resource_type == PONResourceManager.ALLOC_ID or
+                    resource_type == PONResourceManager.GEMPORT_ID):
+                for content in release_content:
+                    self._release_id(resource, content)
+            else:
+                raise Exception("get-resource-failed")
+
+            self._log.debug("Free-" + resource_type + "-success", path=path)
+
+            # Update resource in kv store
+            status = self._update_resource(path, resource)
+
+        except Exception as e:
+            self._log.exception("Free-" + resource_type + "-failed",
+                                path=path, e=e)
+        return status
+
+    def clear_resource_id_pool(self, pon_intf_id, resource_type):
+        """
+        Clear Resource Pool for a given Resource Type on a given PON Port.
+
+        :return boolean: True if removed else False
+        """
+        path = self._get_path(pon_intf_id, resource_type)
+        if path is None:
+            return False
+
+        try:
+            result = self._kv_store.remove_from_kv_store(path)
+            if result is True:
+                self._log.debug("Resource-pool-cleared",
+                                device_id=self.device_id,
+                                path=path)
+                return True
+        except Exception as e:
+            self._log.exception("error-clearing-resource-pool", e=e)
+
+        self._log.error("Clear-resource-pool-failed", device_id=self.device_id,
+                        path=path)
+        return False
+
+    def init_resource_map(self, pon_intf_onu_id):
+        """
+        Initialize resource map
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+        # initialize pon_intf_onu_id tuple to alloc_ids map
+        alloc_id_path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        alloc_ids = list()
+        self._kv_store.update_to_kv_store(
+            alloc_id_path, json.dumps(alloc_ids)
+        )
+
+        # initialize pon_intf_onu_id tuple to gemport_ids map
+        gemport_id_path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        gemport_ids = list()
+        self._kv_store.update_to_kv_store(
+            gemport_id_path, json.dumps(gemport_ids)
+        )
+
+    def remove_resource_map(self, pon_intf_onu_id):
+        """
+        Remove resource map
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+        # remove pon_intf_onu_id tuple to alloc_ids map
+        alloc_id_path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        self._kv_store.remove_from_kv_store(alloc_id_path)
+
+        # remove pon_intf_onu_id tuple to gemport_ids map
+        gemport_id_path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        self._kv_store.remove_from_kv_store(gemport_id_path)
+
+    def get_current_alloc_ids_for_onu(self, pon_intf_onu_id):
+        """
+        Get currently configured alloc ids for given pon_intf_onu_id
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+        path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
+            self.device_id,
+            str(pon_intf_onu_id))
+        value = self._kv_store.get_from_kv_store(path)
+        if value is not None:
+            alloc_id_list = json.loads(value)
+            if len(alloc_id_list) > 0:
+                return alloc_id_list
+
+        return None
+
+    def get_current_gemport_ids_for_onu(self, pon_intf_onu_id):
+        """
+        Get currently configured gemport ids for given pon_intf_onu_id
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+
+        path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
+            self.device_id,
+            str(pon_intf_onu_id))
+        value = self._kv_store.get_from_kv_store(path)
+        if value is not None:
+            gemport_id_list = json.loads(value)
+            if len(gemport_id_list) > 0:
+                return gemport_id_list
+
+        return None
+
+    def update_alloc_ids_for_onu(self, pon_intf_onu_id, alloc_ids):
+        """
+        Update currently configured alloc ids for given pon_intf_onu_id
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+        path = PONResourceManager.ALLOC_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        self._kv_store.update_to_kv_store(
+            path, json.dumps(alloc_ids)
+        )
+
+    def update_gemport_ids_for_onu(self, pon_intf_onu_id, gemport_ids):
+        """
+        Update currently configured gemport ids for given pon_intf_onu_id
+
+        :param pon_intf_onu_id: reference of PON interface id and onu id
+        """
+        path = PONResourceManager.GEMPORT_ID_RESOURCE_MAP_PATH.format(
+            self.device_id, str(pon_intf_onu_id)
+        )
+        self._kv_store.update_to_kv_store(
+            path, json.dumps(gemport_ids)
+        )
+
+    def _get_olt_vendor(self):
+        """
+        Get olt vendor variant
+
+        :return: type of olt vendor
+        """
+        olt_vendor = None
+        if self.extra_args and len(self.extra_args) > 0:
+            parser = OltVendorArgumentParser(add_help=False)
+            parser.add_argument('--olt_vendor', '-o', action='store',
+                                choices=['default', 'asfvolt16', 'cigolt24'],
+                                default='default')
+            try:
+                args = parser.parse_args(shlex.split(self.extra_args))
+                self._log.debug('parsing-extra-arguments', args=args)
+                olt_vendor = args.olt_vendor
+            except ArgumentError as e:
+                self._log.exception('invalid-arguments: {}', e=e)
+            except Exception as e:
+                self._log.exception('option-parsing-error: {}', e=e)
+
+        return olt_vendor
+
+    def _generate_next_id(self, resource):
+        """
+        Generate unique id having OFFSET as start index.
+
+        :param resource: resource used to generate ID
+        :return int: generated id
+        """
+        pos = resource[PONResourceManager.POOL].find('0b0')
+        resource[PONResourceManager.POOL].set(1, pos)
+        return pos[0] + resource[PONResourceManager.START_IDX]
+
+    def _release_id(self, resource, unique_id):
+        """
+        Release unique id having OFFSET as start index.
+
+        :param resource: resource used to release ID
+        :param unique_id: id need to be released
+        """
+        pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
+        resource[PONResourceManager.POOL].set(0, pos)
+
+    def _get_path(self, pon_intf_id, resource_type):
+        """
+        Get path for given resource type.
+
+        :param pon_intf_id: OLT PON interface id
+        :param resource_type: String to identify type of resource
+        :return: path for given resource type
+        """
+        path = None
+        if resource_type == PONResourceManager.ONU_ID:
+            path = self._get_onu_id_resource_path(pon_intf_id)
+        elif resource_type == PONResourceManager.ALLOC_ID:
+            path = self._get_alloc_id_resource_path(pon_intf_id)
+        elif resource_type == PONResourceManager.GEMPORT_ID:
+            path = self._get_gemport_id_resource_path(pon_intf_id)
+        else:
+            self._log.error("invalid-resource-pool-identifier")
+        return path
+
+    def _get_alloc_id_resource_path(self, pon_intf_id):
+        """
+        Get alloc id resource path.
+
+        :param pon_intf_id: OLT PON interface id
+        :return: alloc id resource path
+        """
+        return PONResourceManager.ALLOC_ID_POOL_PATH.format(
+            self.device_id, pon_intf_id)
+
+    def _get_gemport_id_resource_path(self, pon_intf_id):
+        """
+        Get gemport id resource path.
+
+        :param pon_intf_id: OLT PON interface id
+        :return: gemport id resource path
+        """
+        return PONResourceManager.GEMPORT_ID_POOL_PATH.format(
+            self.device_id, pon_intf_id)
+
+    def _get_onu_id_resource_path(self, pon_intf_id):
+        """
+        Get onu id resource path.
+
+        :param pon_intf_id: OLT PON interface id
+        :return: onu id resource path
+        """
+        return PONResourceManager.ONU_ID_POOL_PATH.format(
+            self.device_id, pon_intf_id)
+
+    def _update_resource(self, path, resource):
+        """
+        Update resource in resource kv store.
+
+        :param path: path to update resource
+        :param resource: resource need to be updated
+        :return boolean: True if resource updated in kv store else False
+        """
+        resource[PONResourceManager.POOL] = \
+            resource[PONResourceManager.POOL].bin
+        result = self._kv_store.update_to_kv_store(path, json.dumps(resource))
+        if result is True:
+            return True
+        return False
+
+    def _get_resource(self, path):
+        """
+        Get resource from kv store.
+
+        :param path: path to get resource
+        :return: resource if resource present in kv store else None
+        """
+        # get resource from kv store
+        result = self._kv_store.get_from_kv_store(path)
+        if result is None:
+            return result
+        self._log.info("dumping resource", result=result)
+        resource = result
+
+        if resource is not None:
+            # decode resource fetched from backend store to dictionary
+            resource = json.loads(resource)
+
+            # resource pool in backend store stored as binary string whereas to
+            # access the pool to generate/release IDs it need to be converted
+            # as BitArray
+            resource[PONResourceManager.POOL] = \
+                BitArray('0b' + resource[PONResourceManager.POOL])
+
+        return resource
+
+    def _format_resource(self, pon_intf_id, start_idx, end_idx):
+        """
+        Format resource as json.
+
+        :param pon_intf_id: OLT PON interface id
+        :param start_idx: start index for id pool
+        :param end_idx: end index for id pool
+        :return dictionary: resource formatted as dictionary
+        """
+        # Format resource as json to be stored in backend store
+        resource = dict()
+        resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+        resource[PONResourceManager.START_IDX] = start_idx
+        resource[PONResourceManager.END_IDX] = end_idx
+
+        # resource pool stored in backend store as binary string
+        resource[PONResourceManager.POOL] = BitArray(end_idx).bin
+
+        return json.dumps(resource)
diff --git a/python/common/structlog_setup.py b/python/common/structlog_setup.py
new file mode 100644
index 0000000..cbbda89
--- /dev/null
+++ b/python/common/structlog_setup.py
@@ -0,0 +1,132 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Setting up proper logging for Voltha"""
+
+import logging
+import logging.config
+from collections import OrderedDict
+
+import structlog
+from structlog.stdlib import BoundLogger, INFO
+
+try:
+    from thread import get_ident as _get_ident
+except ImportError:
+    from dummy_thread import get_ident as _get_ident
+
+
+class StructuredLogRenderer(object):
+    def __call__(self, logger, name, event_dict):
+        # in order to keep structured log data in event_dict to be forwarded as
+        # is, we need to pass it into the logger framework as the first
+        # positional argument.
+        args = (event_dict,)
+        kwargs = {}
+        return args, kwargs
+
+
+class PlainRenderedOrderedDict(OrderedDict):
+    """Our special version of OrderedDict that renders into string as a dict,
+       to make the log stream output cleaner.
+    """
+    def __repr__(self, _repr_running={}):
+        'od.__repr__() <==> repr(od)'
+        call_key = id(self), _get_ident()
+        if call_key in _repr_running:
+            return '...'
+        _repr_running[call_key] = 1
+        try:
+            if not self:
+                return '{}'
+            return '{%s}' % ", ".join("%s: %s" % (k, v)
+                                      for k, v in self.items())
+        finally:
+            del _repr_running[call_key]
+
+
+def setup_logging(log_config, instance_id, verbosity_adjust=0):
+    """
+    Set up logging such that:
+    - The primary logging entry method is structlog
+      (see http://structlog.readthedocs.io/en/stable/index.html)
+    - By default, the logging backend is Python standard lib logger
+    """
+
+    def add_exc_info_flag_for_exception(_, name, event_dict):
+        if name == 'exception':
+            event_dict['exc_info'] = True
+        return event_dict
+
+    def add_instance_id(_, __, event_dict):
+        event_dict['instance_id'] = instance_id
+        return event_dict
+
+    # Configure standard logging
+    logging.config.dictConfig(log_config)
+    logging.root.level -= 10 * verbosity_adjust
+
+    processors = [
+        add_exc_info_flag_for_exception,
+        structlog.processors.StackInfoRenderer(),
+        structlog.processors.format_exc_info,
+        add_instance_id,
+        StructuredLogRenderer(),
+    ]
+    structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(),
+                        context_class=PlainRenderedOrderedDict,
+                        wrapper_class=BoundLogger,
+                        processors=processors)
+
+    # Mark first line of log
+    log = structlog.get_logger()
+    log.info("first-line")
+    return log
+
+
+def update_logging(instance_id, vcore_id):
+    """
+    Add the vcore id to the structured logger
+    :param vcore_id:  The assigned vcore id
+    :return: structure logger
+    """
+    def add_exc_info_flag_for_exception(_, name, event_dict):
+        if name == 'exception':
+            event_dict['exc_info'] = True
+        return event_dict
+
+    def add_instance_id(_, __, event_dict):
+        event_dict['instance_id'] = instance_id
+        return event_dict
+
+    def add_vcore_id(_, __, event_dict):
+        event_dict['vcore_id'] = vcore_id
+        return event_dict
+
+    processors = [
+        add_exc_info_flag_for_exception,
+        structlog.processors.StackInfoRenderer(),
+        structlog.processors.format_exc_info,
+        add_instance_id,
+        add_vcore_id,
+        StructuredLogRenderer(),
+    ]
+    structlog.configure(processors=processors)
+
+    # Mark first line of log
+    log = structlog.get_logger()
+    log.info("updated-logger")
+    return log
diff --git a/adapters/common/utils/__init__.py b/python/common/utils/__init__.py
similarity index 100%
copy from adapters/common/utils/__init__.py
copy to python/common/utils/__init__.py
diff --git a/adapters/common/utils/asleep.py b/python/common/utils/asleep.py
similarity index 100%
copy from adapters/common/utils/asleep.py
copy to python/common/utils/asleep.py
diff --git a/python/common/utils/consulhelpers.py b/python/common/utils/consulhelpers.py
new file mode 100644
index 0000000..df4dd58
--- /dev/null
+++ b/python/common/utils/consulhelpers.py
@@ -0,0 +1,178 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Some consul related convenience functions
+"""
+
+from structlog import get_logger
+from consul import Consul
+from random import randint
+from common.utils.nethelpers import get_my_primary_local_ipv4
+
+log = get_logger()
+
+
+def connect_to_consult(consul_endpoint):
+    log.debug('getting-service-endpoint', consul=consul_endpoint)
+
+    host = consul_endpoint.split(':')[0].strip()
+    port = int(consul_endpoint.split(':')[1].strip())
+
+    return Consul(host=host, port=port)
+
+
+def verify_all_services_healthy(consul_endpoint, service_name=None,
+                                number_of_expected_services=None):
+    """
+    Verify in consul if any service is healthy
+    :param consul_endpoint: a <host>:<port> string
+    :param service_name: name of service to check, optional
+    :param number_of_expected_services number of services to check for, optional
+    :return: true if healthy, false otherwise
+    """
+
+    def check_health(service):
+        _, serv_health = consul.health.service(service, passing=True)
+        return not serv_health == []
+
+    consul = connect_to_consult(consul_endpoint)
+
+    if service_name is not None:
+        return check_health(service_name)
+
+    services = get_all_services(consul_endpoint)
+
+    items = services.keys()
+
+    if number_of_expected_services is not None and \
+                    len(items) != number_of_expected_services:
+        return False
+
+    for item in items:
+        if not check_health(item):
+            return False
+
+    return True
+
+
+def get_all_services(consul_endpoint):
+    log.debug('getting-service-verify-health')
+
+    consul = connect_to_consult(consul_endpoint)
+    _, services = consul.catalog.services()
+
+    return services
+
+
+def get_all_instances_of_service(consul_endpoint, service_name):
+    log.debug('getting-all-instances-of-service', service=service_name)
+
+    consul = connect_to_consult(consul_endpoint)
+    _, services = consul.catalog.service(service_name)
+
+    for service in services:
+        log.debug('service',
+                  name=service['ServiceName'],
+                  serviceid=service['ServiceID'],
+                  serviceport=service['ServicePort'],
+                  createindex=service['CreateIndex'])
+
+    return services
+
+
+def get_endpoint_from_consul(consul_endpoint, service_name):
+    """
+    Get endpoint of service_name from consul.
+    :param consul_endpoint: a <host>:<port> string
+    :param service_name: name of service for which endpoint
+                         needs to be found.
+    :return: service endpoint if available, else exit.
+    """
+    log.debug('getting-service-info', service=service_name)
+
+    consul = connect_to_consult(consul_endpoint)
+    _, services = consul.catalog.service(service_name)
+
+    if len(services) == 0:
+        raise Exception(
+            'Cannot find service {} in consul'.format(service_name))
+        os.exit(1)
+
+    """ Get host IPV4 address
+    """
+    local_ipv4 = get_my_primary_local_ipv4()
+    """ If host IP address from where the request came in matches
+        the IP address of the requested service's host IP address,
+        pick the endpoint
+    """
+    for i in range(len(services)):
+        service = services[i]
+        if service['ServiceAddress'] == local_ipv4:
+            log.debug("picking address locally")
+            endpoint = '{}:{}'.format(service['ServiceAddress'],
+                                      service['ServicePort'])
+            return endpoint
+
+    """ If service is not available locally, picak a random
+        endpoint for the service from the list
+    """
+    service = services[randint(0, len(services) - 1)]
+    endpoint = '{}:{}'.format(service['ServiceAddress'],
+                              service['ServicePort'])
+
+    return endpoint
+
+
+def get_healthy_instances(consul_endpoint, service_name=None,
+                          number_of_expected_services=None):
+    """
+    Verify in consul if any service is healthy
+    :param consul_endpoint: a <host>:<port> string
+    :param service_name: name of service to check, optional
+    :param number_of_expected_services number of services to check for, optional
+    :return: true if healthy, false otherwise
+    """
+
+    def check_health(service):
+        _, serv_health = consul.health.service(service, passing=True)
+        return not serv_health == []
+
+    consul = connect_to_consult(consul_endpoint)
+
+    if service_name is not None:
+        return check_health(service_name)
+
+    services = get_all_services(consul_endpoint)
+
+    items = services.keys()
+
+    if number_of_expected_services is not None and \
+                    len(items) != number_of_expected_services:
+        return False
+
+    for item in items:
+        if not check_health(item):
+            return False
+
+    return True
+
+
+if __name__ == '__main__':
+    # print get_endpoint_from_consul('10.100.198.220:8500', 'kafka')
+    # print get_healthy_instances('10.100.198.220:8500', 'voltha-health')
+    # print get_healthy_instances('10.100.198.220:8500')
+    get_all_instances_of_service('10.100.198.220:8500', 'voltha-grpc')
diff --git a/adapters/common/utils/deferred_utils.py b/python/common/utils/deferred_utils.py
similarity index 100%
copy from adapters/common/utils/deferred_utils.py
copy to python/common/utils/deferred_utils.py
diff --git a/adapters/common/utils/dockerhelpers.py b/python/common/utils/dockerhelpers.py
similarity index 100%
copy from adapters/common/utils/dockerhelpers.py
copy to python/common/utils/dockerhelpers.py
diff --git a/adapters/common/utils/grpc_utils.py b/python/common/utils/grpc_utils.py
similarity index 100%
copy from adapters/common/utils/grpc_utils.py
copy to python/common/utils/grpc_utils.py
diff --git a/adapters/common/utils/id_generation.py b/python/common/utils/id_generation.py
similarity index 100%
copy from adapters/common/utils/id_generation.py
copy to python/common/utils/id_generation.py
diff --git a/adapters/common/utils/indexpool.py b/python/common/utils/indexpool.py
similarity index 100%
copy from adapters/common/utils/indexpool.py
copy to python/common/utils/indexpool.py
diff --git a/adapters/common/utils/json_format.py b/python/common/utils/json_format.py
similarity index 100%
copy from adapters/common/utils/json_format.py
copy to python/common/utils/json_format.py
diff --git a/adapters/common/utils/message_queue.py b/python/common/utils/message_queue.py
similarity index 100%
copy from adapters/common/utils/message_queue.py
copy to python/common/utils/message_queue.py
diff --git a/adapters/common/utils/nethelpers.py b/python/common/utils/nethelpers.py
similarity index 100%
copy from adapters/common/utils/nethelpers.py
copy to python/common/utils/nethelpers.py
diff --git a/adapters/common/utils/ordered_weakvalue_dict.py b/python/common/utils/ordered_weakvalue_dict.py
similarity index 100%
copy from adapters/common/utils/ordered_weakvalue_dict.py
copy to python/common/utils/ordered_weakvalue_dict.py
diff --git a/adapters/docker/Dockerfile.base b/python/docker/Dockerfile.base
similarity index 100%
copy from adapters/docker/Dockerfile.base
copy to python/docker/Dockerfile.base
diff --git a/python/docker/Dockerfile.ofagent b/python/docker/Dockerfile.ofagent
new file mode 100644
index 0000000..48b4728
--- /dev/null
+++ b/python/docker/Dockerfile.ofagent
@@ -0,0 +1,45 @@
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+ARG TAG=latest
+ARG REGISTRY=
+ARG REPOSITORY=
+
+FROM ${REGISTRY}${REPOSITORY}voltha-base:${TAG}
+
+MAINTAINER Voltha Community <info@opennetworking.org>
+
+# Install protoc version 3.0.0; this is not yet the supported
+# version on xenial, so we need to "backport" it
+RUN apt-get update && \
+    apt-get install -y zlib1g-dev wget && \
+    wget http://ftp.us.debian.org/debian/pool/main/p/protobuf/libprotoc10_3.0.0-9_amd64.deb && \
+    wget http://ftp.us.debian.org/debian/pool/main/p/protobuf/libprotobuf-lite10_3.0.0-9_amd64.deb && \
+    wget http://ftp.us.debian.org/debian/pool/main/p/protobuf/libprotobuf-dev_3.0.0-9_amd64.deb && \
+    wget http://ftp.us.debian.org/debian/pool/main/p/protobuf/libprotobuf10_3.0.0-9_amd64.deb && \
+    wget http://ftp.us.debian.org/debian/pool/main/p/protobuf/protobuf-compiler_3.0.0-9_amd64.deb && \
+    dpkg -i *.deb && \
+    protoc --version && \
+    rm -f *.deb
+
+# Bundle app source
+RUN mkdir /ofagent && touch /ofagent/__init__.py
+ENV PYTHONPATH=/ofagent
+COPY common /ofagent/common
+COPY ofagent /ofagent/ofagent
+COPY pki /ofagent/pki
+
+ENTRYPOINT ["/usr/bin/dumb-init", "--"]
+
+# Exposing process and default entry point
+CMD ["dumb-init", "python", "ofagent/ofagent/main.py"]
diff --git a/adapters/common/__init__.py b/python/ofagent/__init__.py
old mode 100644
new mode 100755
similarity index 100%
copy from adapters/common/__init__.py
copy to python/ofagent/__init__.py
diff --git a/python/ofagent/agent.py b/python/ofagent/agent.py
new file mode 100755
index 0000000..6117c8f
--- /dev/null
+++ b/python/ofagent/agent.py
@@ -0,0 +1,206 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import sys
+
+import structlog
+import os.path
+from twisted.internet import protocol, reactor, ssl
+from twisted.internet.defer import Deferred, inlineCallbacks
+
+import loxi.of13 as of13
+from common.utils.asleep import asleep
+from of_connection import OpenFlowConnection
+from of_protocol_handler import OpenFlowProtocolHandler
+
+log = structlog.get_logger()
+
+
+class Agent(protocol.ClientFactory):
+
+    generation_is_defined = False
+    cached_generation_id = None
+
+    def __init__(self,
+                 controller_endpoint,
+                 datapath_id,
+                 device_id,
+                 rpc_stub,
+                 enable_tls=False,
+                 key_file=None,
+                 cert_file=None,
+                 conn_retry_interval=1):
+
+        self.controller_endpoint = controller_endpoint
+        self.datapath_id = datapath_id
+        self.device_id = device_id
+        self.rpc_stub = rpc_stub
+        self.enable_tls = enable_tls
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.retry_interval = conn_retry_interval
+
+        self.running = False
+        self.connector = None # will be a Connector instance once connected
+        self.d_disconnected = None  # a deferred to signal reconnect loop when
+                                    # TCP connection is lost
+        self.connected = False
+        self.exiting = False
+        self.proto_handler = None
+
+    def get_device_id(self):
+        return self.device_id
+
+    def start(self):
+        log.debug('starting')
+        if self.running:
+            return
+        self.running = True
+        reactor.callLater(0, self.keep_connected)
+        log.info('started')
+        return self
+
+    def stop(self):
+        log.debug('stopping')
+        self.connected = False
+        self.exiting = True
+        self.connector.disconnect()
+        log.info('stopped')
+
+    def resolve_endpoint(self, endpoint):
+        # enable optional resolution via consul;
+        # see https://jira.opencord.org/browse/CORD-820
+        host, port = endpoint.split(':', 2)
+        return host, int(port)
+
+    @inlineCallbacks
+    def keep_connected(self):
+        """Keep reconnecting to the controller"""
+        while not self.exiting:
+            host, port = self.resolve_endpoint(self.controller_endpoint)
+            log.info('connecting', host=host, port=port)
+            if self.enable_tls:
+                try:
+                    # Check that key_file and cert_file is provided and
+                    # the files exist
+                    if self.key_file is None or             \
+                       self.cert_file is None or            \
+                       not os.path.isfile(self.key_file) or \
+                       not os.path.isfile(self.cert_file):
+                        raise Exception('key_file "{}" or cert_file "{}"'
+                                        ' is not found'.
+                                         format(self.key_file, self.cert_file))
+                    with open(self.key_file) as keyFile:
+                        with open(self.cert_file) as certFile:
+                            clientCert = ssl.PrivateCertificate.loadPEM(
+                                keyFile.read() + certFile.read())
+
+                    ctx = clientCert.options()
+                    self.connector = reactor.connectSSL(host, port, self, ctx)
+                    log.info('tls-enabled')
+
+                except Exception as e:
+                    log.exception('failed-to-connect', reason=e)
+            else:
+                self.connector = reactor.connectTCP(host, port, self)
+                log.info('tls-disabled')
+
+            self.d_disconnected = Deferred()
+            yield self.d_disconnected
+            log.debug('reconnect', after_delay=self.retry_interval)
+            yield asleep(self.retry_interval)
+
+    def enter_disconnected(self, event, reason):
+        """Internally signal entering disconnected state"""
+        self.connected = False
+        if not self.exiting:
+            log.error(event, reason=reason)
+            self.d_disconnected.callback(None)
+
+    def enter_connected(self):
+        """Handle transitioning from disconnected to connected state"""
+        log.info('connected')
+        self.connected = True
+        self.read_buffer = None
+        reactor.callLater(0, self.proto_handler.start)
+
+    # protocol.ClientFactory methods
+
+    def protocol(self):
+        cxn = OpenFlowConnection(self)  # Low level message handler
+        self.proto_handler = OpenFlowProtocolHandler(
+            self.datapath_id, self.device_id, self, cxn, self.rpc_stub)
+        return cxn
+
+    def clientConnectionFailed(self, connector, reason):
+        self.enter_disconnected('connection-failed', reason)
+
+    def clientConnectionLost(self, connector, reason):
+        if not self.exiting:
+            log.error('client-connection-lost',
+                      reason=reason, connector=connector)
+
+    def forward_packet_in(self, ofp_packet_in):
+        if self.proto_handler is not None:
+            self.proto_handler.forward_packet_in(ofp_packet_in)
+
+    def forward_change_event(self, event):
+        # assert isinstance(event, ChangeEvent)
+        log.info('got-change-event', change_event=event)
+        if event.HasField("port_status"):
+            if self.proto_handler is not None:
+                self.proto_handler.forward_port_status(event.port_status)
+        else:
+            log.error('unknown-change-event', change_event=event)
+
+
+if __name__ == '__main__':
+    """Run this to test the agent for N concurrent sessions:
+       python agent [<number-of-desired-instances>]
+    """
+
+    n = 1 if len(sys.argv) < 2 else int(sys.argv[1])
+
+    from utils import mac_str_to_tuple
+
+    class MockRpc(object):
+        @staticmethod
+        def get_port_list(_):
+            ports = []
+            cap = of13.OFPPF_1GB_FD | of13.OFPPF_FIBER
+            for pno, mac, nam, cur, adv, sup, spe in (
+                    (1, '00:00:00:00:00:01', 'onu1', cap, cap, cap,
+                     of13.OFPPF_1GB_FD),
+                    (2, '00:00:00:00:00:02', 'onu2', cap, cap, cap,
+                     of13.OFPPF_1GB_FD),
+                    (129, '00:00:00:00:00:81', 'olt', cap, cap, cap,
+                     of13.OFPPF_1GB_FD)
+            ):
+                port = of13.common.port_desc(pno, mac_str_to_tuple(mac), nam,
+                                             curr=cur, advertised=adv,
+                                             supported=sup,
+                                             curr_speed=spe, max_speed=spe)
+                ports.append(port)
+            return ports
+
+    stub = MockRpc()
+    agents = [Agent('localhost:6653', 256 + i, stub).start() for i in range(n)]
+
+    def shutdown():
+        [a.stop() for a in agents]
+
+    reactor.addSystemEventTrigger('before', 'shutdown', shutdown)
+    reactor.run()
diff --git a/python/ofagent/connection_mgr.py b/python/ofagent/connection_mgr.py
new file mode 100755
index 0000000..b960b69
--- /dev/null
+++ b/python/ofagent/connection_mgr.py
@@ -0,0 +1,329 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+
+import sys
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
+
+from common.utils.asleep import asleep
+from common.utils.consulhelpers import get_endpoint_from_consul
+from structlog import get_logger
+import grpc
+from grpc import StatusCode
+from grpc._channel import _Rendezvous
+from ofagent.protos import third_party
+from protos import voltha_pb2
+from protos.voltha_pb2 import OfAgentSubscriber
+from grpc_client import GrpcClient
+
+from agent import Agent
+from common.utils.dockerhelpers import get_my_containers_name
+
+
+log = get_logger()
+# _ = third_party
+
+class ConnectionManager(object):
+    def __init__(self, consul_endpoint, vcore_endpoint, vcore_grpc_timeout,
+                 controller_endpoints, instance_id,
+                 enable_tls=False, key_file=None, cert_file=None,
+                 vcore_retry_interval=0.5, devices_refresh_interval=5,
+                 subscription_refresh_interval=5):
+
+        log.info('init-connection-manager')
+        log.info('list-of-controllers', controller_endpoints=controller_endpoints)
+        self.controller_endpoints = controller_endpoints
+        self.consul_endpoint = consul_endpoint
+        self.vcore_endpoint = vcore_endpoint
+        self.grpc_timeout = vcore_grpc_timeout
+        self.instance_id = instance_id
+        self.enable_tls = enable_tls
+        self.key_file = key_file
+        self.cert_file = cert_file
+
+        self.channel = None
+        self.grpc_client = None  # single, shared gRPC client to vcore
+
+        self.agent_map = {}  # (datapath_id, controller_endpoint) -> Agent()
+        self.device_id_to_datapath_id_map = {}
+
+        self.vcore_retry_interval = vcore_retry_interval
+        self.devices_refresh_interval = devices_refresh_interval
+        self.subscription_refresh_interval = subscription_refresh_interval
+        self.subscription = None
+
+        self.running = False
+
+    def start(self):
+
+        if self.running:
+            return
+
+        log.debug('starting')
+
+        self.running = True
+
+        # Get a subscription to vcore
+        reactor.callInThread(self.get_vcore_subscription)
+
+        # Start monitoring logical devices and manage agents accordingly
+        reactor.callLater(0, self.monitor_logical_devices)
+
+        log.info('started')
+
+        return self
+
+    def stop(self):
+        log.debug('stopping')
+        # clean up all controller connections
+        for agent in self.agent_map.itervalues():
+            agent.stop()
+        self.running = False
+
+        self._reset_grpc_attributes()
+
+        log.info('stopped')
+
+    def resolve_endpoint(self, endpoint):
+        ip_port_endpoint = endpoint
+        if endpoint.startswith('@'):
+            try:
+                ip_port_endpoint = get_endpoint_from_consul(
+                    self.consul_endpoint, endpoint[1:])
+                log.info(
+                    '{}-service-endpoint-found'.format(endpoint), address=ip_port_endpoint)
+            except Exception as e:
+                log.error('{}-service-endpoint-not-found'.format(endpoint), exception=repr(e))
+                log.error('committing-suicide')
+                # Committing suicide in order to let docker restart ofagent
+                os.system("kill -15 {}".format(os.getpid()))
+        if ip_port_endpoint:
+            host, port = ip_port_endpoint.split(':', 2)
+            return host, int(port)
+
+    def _reset_grpc_attributes(self):
+        log.debug('start-reset-grpc-attributes')
+
+        if self.grpc_client is not None:
+            self.grpc_client.stop()
+
+        if self.channel is not None:
+            del self.channel
+
+        self.is_alive = False
+        self.channel = None
+        self.subscription = None
+        self.grpc_client = None
+
+        log.debug('stop-reset-grpc-attributes')
+
+    def _assign_grpc_attributes(self):
+        log.debug('start-assign-grpc-attributes')
+
+        host, port = self.resolve_endpoint(self.vcore_endpoint)
+        log.info('revolved-vcore-endpoint', endpoint=self.vcore_endpoint, host=host, port=port)
+
+        assert host is not None
+        assert port is not None
+
+        # Establish a connection to the vcore GRPC server
+        self.channel = grpc.insecure_channel('{}:{}'.format(host, port))
+        self.is_alive = True
+
+        log.debug('stop-assign-grpc-attributes')
+
+    @inlineCallbacks
+    def get_vcore_subscription(self):
+        log.debug('start-get-vcore-subscription')
+
+        while self.running and self.subscription is None:
+            try:
+                # If a subscription is not yet assigned then establish new GRPC connection
+                # ... otherwise keep using existing connection details
+                if self.subscription is None:
+                    self._assign_grpc_attributes()
+
+                # Send subscription request to register the current ofagent instance
+                container_name = self.instance_id
+                if self.grpc_client is None:
+                    self.grpc_client = GrpcClient(self, self.channel, self.grpc_timeout)
+                subscription = yield self.grpc_client.subscribe(
+                    OfAgentSubscriber(ofagent_id=container_name))
+
+                # If the subscriber id matches the current instance
+                # ... then the subscription has succeeded
+                if subscription is not None and subscription.ofagent_id == container_name:
+                    if self.subscription is None:
+                        # Keep details on the current GRPC session and subscription
+                        log.debug('subscription-with-vcore-successful', subscription=subscription)
+                        self.subscription = subscription
+                        self.grpc_client.start()
+
+                    # Sleep a bit in between each subscribe
+                    yield asleep(self.subscription_refresh_interval)
+
+                    # Move on to next subscribe request
+                    continue
+
+                # The subscription did not succeed, reset and move on
+                else:
+                    log.info('subscription-with-vcore-unavailable', subscription=subscription)
+
+            except _Rendezvous, e:
+                log.error('subscription-with-vcore-terminated',exception=e, status=e.code())
+
+            except Exception as e:
+                log.exception('unexpected-subscription-termination-with-vcore', e=e)
+
+            # Reset grpc details
+            # The vcore instance is either not available for subscription
+            # or a failure occurred with the existing communication.
+            self._reset_grpc_attributes()
+
+            # Sleep for a short period and retry
+            yield asleep(self.vcore_retry_interval)
+
+        log.debug('stop-get-vcore-subscription')
+
+    @inlineCallbacks
+    def get_list_of_logical_devices_from_voltha(self):
+
+        while self.running:
+            log.info('retrieve-logical-device-list')
+            try:
+                devices = yield \
+                    self.grpc_client.list_logical_devices()
+
+                for device in devices:
+                    log.info("logical-device-entry", id=device.id,
+                             datapath_id=device.datapath_id)
+
+                returnValue(devices)
+
+            except _Rendezvous, e:
+                status = e.code()
+                log.error('vcore-communication-failure', exception=e, status=status)
+                if status == StatusCode.UNAVAILABLE or status == StatusCode.DEADLINE_EXCEEDED:
+                    os.system("kill -15 {}".format(os.getpid()))
+
+            except Exception as e:
+                log.exception('logical-devices-retrieval-failure', exception=e)
+
+            log.info('reconnect', after_delay=self.vcore_retry_interval)
+            yield asleep(self.vcore_retry_interval)
+
+    def refresh_agent_connections(self, devices):
+        """
+        Based on the new device list, update the following state in the class:
+        * agent_map
+        * datapath_map
+        * device_id_map
+        :param devices: full device list freshly received from Voltha
+        :return: None
+        """
+
+        # Use datapath ids for deciding what's new and what's obsolete
+        desired_datapath_ids = set(d.datapath_id for d in devices)
+        current_datapath_ids = set(datapath_ids[0] for datapath_ids in self.agent_map.iterkeys())
+
+        # if identical, nothing to do
+        if desired_datapath_ids == current_datapath_ids:
+            return
+
+        # ... otherwise calculate differences
+        to_add = desired_datapath_ids.difference(current_datapath_ids)
+        to_del = current_datapath_ids.difference(desired_datapath_ids)
+
+        # remove what we don't need
+        for datapath_id in to_del:
+            self.delete_agent(datapath_id)
+
+        # start new agents as needed
+        for device in devices:
+            if device.datapath_id in to_add:
+                self.create_agent(device)
+
+        log.debug('updated-agent-list', count=len(self.agent_map))
+        log.debug('updated-device-id-to-datapath-id-map',
+                  map=str(self.device_id_to_datapath_id_map))
+
+    def create_agent(self, device):
+        datapath_id = device.datapath_id
+        device_id = device.id
+        for controller_endpoint in self.controller_endpoints:
+            agent = Agent(controller_endpoint, datapath_id,
+                          device_id, self.grpc_client, self.enable_tls,
+                          self.key_file, self.cert_file)
+            agent.start()
+            self.agent_map[(datapath_id,controller_endpoint)] = agent
+            self.device_id_to_datapath_id_map[device_id] = datapath_id
+
+    def delete_agent(self, datapath_id):
+        for controller_endpoint in self.controller_endpoints:
+            agent = self.agent_map[(datapath_id,controller_endpoint)]
+            device_id = agent.get_device_id()
+            agent.stop()
+            del self.agent_map[(datapath_id,controller_endpoint)]
+            del self.device_id_to_datapath_id_map[device_id]
+
+    @inlineCallbacks
+    def monitor_logical_devices(self):
+        log.debug('start-monitor-logical-devices')
+
+        while self.running:
+            log.info('monitoring-logical-devices')
+
+            # should change to a gRPC streaming call
+            # see https://jira.opencord.org/browse/CORD-821
+
+            try:
+                if self.channel is not None and self.grpc_client is not None and \
+                                self.subscription is not None:
+                    # get current list from Voltha
+                    devices = yield \
+                        self.get_list_of_logical_devices_from_voltha()
+
+                    # update agent list and mapping tables as needed
+                    self.refresh_agent_connections(devices)
+                else:
+                    log.info('vcore-communication-unavailable')
+
+                # wait before next poll
+                yield asleep(self.devices_refresh_interval)
+
+            except _Rendezvous, e:
+                log.error('vcore-communication-failure', exception=repr(e), status=e.code())
+
+            except Exception as e:
+                log.exception('unexpected-vcore-communication-failure', exception=repr(e))
+
+        log.debug('stop-monitor-logical-devices')
+
+    def forward_packet_in(self, device_id, ofp_packet_in):
+        datapath_id = self.device_id_to_datapath_id_map.get(device_id, None)
+        if datapath_id:
+            for controller_endpoint in self.controller_endpoints:
+                agent = self.agent_map[(datapath_id, controller_endpoint)]
+                agent.forward_packet_in(ofp_packet_in)
+
+    def forward_change_event(self, device_id, event):
+        datapath_id = self.device_id_to_datapath_id_map.get(device_id, None)
+        if datapath_id:
+            for controller_endpoint in self.controller_endpoints:
+                agent = self.agent_map[(datapath_id, controller_endpoint)]
+                agent.forward_change_event(event)
diff --git a/python/ofagent/converter.py b/python/ofagent/converter.py
new file mode 100755
index 0000000..192a65a
--- /dev/null
+++ b/python/ofagent/converter.py
@@ -0,0 +1,436 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Convert loxi objects to openflow_13 messages and back.
+"""
+from copy import copy
+
+from google.protobuf.descriptor import FieldDescriptor
+
+import loxi.of13 as of13
+from protobuf_to_dict import protobuf_to_dict, TYPE_CALLABLE_MAP
+from protos import openflow_13_pb2 as pb2
+
+
+type_callable_map = copy(TYPE_CALLABLE_MAP)
+type_callable_map.update({
+    FieldDescriptor.TYPE_STRING: str
+})
+
+def pb2dict(pb):
+    """
+    Convert protobuf to a dict of values good for instantiating
+    loxi objects (or any other objects). We specialize the protobuf_to_dict
+    library call with our modified decoders.
+    :param pb: protobuf as loaded into Python
+    :return: dict of values
+    """
+    return protobuf_to_dict(pb, type_callable_map)
+
+def to_loxi(grpc_object):
+    cls = grpc_object.__class__
+    converter = to_loxi_converters[cls.__name__]
+    return converter(grpc_object)
+
+def to_grpc(loxi_object):
+    cls = loxi_object.__class__
+    converter = to_grpc_converters[cls]
+    return converter(loxi_object)
+
+def ofp_port_to_loxi_port_desc(pb):
+    kw = pb2dict(pb)
+    return of13.common.port_desc(**kw)
+
+def ofp_port_status_to_loxi_port_status(pb):
+    return of13.message.port_status(
+        reason=pb.reason,
+        desc=ofp_port_to_loxi_port_desc(pb.desc)
+    )
+
+def ofp_port_stats_to_loxi_port_stats(pb):
+    kw = pb2dict(pb)
+    return of13.port_stats_entry(**kw)
+
+def make_loxi_field(oxm_field):
+    assert oxm_field['oxm_class'] == pb2.OFPXMC_OPENFLOW_BASIC
+    ofb_field = oxm_field['ofb_field']
+    field_type = ofb_field.get('type', 0)
+
+    if field_type == pb2.OFPXMT_OFB_ETH_TYPE:
+        return (
+            of13.oxm.eth_type(value=ofb_field['eth_type']))
+
+    elif field_type == pb2.OFPXMT_OFB_IN_PORT:
+        return (
+            of13.oxm.in_port(value=ofb_field['port']))
+
+    elif field_type == pb2.OFPXMT_OFB_IP_PROTO:
+        return (
+            of13.oxm.ip_proto(value=ofb_field['ip_proto']))
+
+    elif field_type == pb2.OFPXMT_OFB_VLAN_VID:
+        return (
+            of13.oxm.vlan_vid(value=ofb_field['vlan_vid']))
+
+    elif field_type == pb2.OFPXMT_OFB_VLAN_PCP:
+        return (
+            of13.oxm.vlan_pcp(value=ofb_field['vlan_pcp']))
+
+    elif field_type == pb2.OFPXMT_OFB_IPV4_SRC:
+        return (
+            of13.oxm.ipv4_src(value=ofb_field['ipv4_src']))
+
+    elif field_type == pb2.OFPXMT_OFB_IPV4_DST:
+        return (
+            of13.oxm.ipv4_dst(value=ofb_field['ipv4_dst']))
+
+    elif field_type == pb2.OFPXMT_OFB_UDP_SRC:
+        return (
+            of13.oxm.udp_src(value=ofb_field['udp_src']))
+
+    elif field_type == pb2.OFPXMT_OFB_UDP_DST:
+        return (
+            of13.oxm.udp_dst(value=ofb_field['udp_dst']))
+
+    elif field_type == pb2.OFPXMT_OFB_METADATA:
+        return (
+            of13.oxm.metadata(value=ofb_field['table_metadata']))
+
+    else:
+        raise NotImplementedError(
+            'OXM match field for type %s' % field_type)
+
+def make_loxi_match(match):
+    assert match.get('type', pb2.OFPMT_STANDARD) == pb2.OFPMT_OXM
+    loxi_match_fields = []
+    for oxm_field in match.get('oxm_fields', []):
+        loxi_match_fields.append(make_loxi_field(oxm_field))
+    return of13.match_v3(oxm_list=loxi_match_fields)
+
+
+def make_loxi_action(a):
+    if type(a) is not dict:
+        a = pb2dict(a)
+
+    typ = a.get('type', 0)
+
+    if typ == pb2.OFPAT_OUTPUT:
+        output_kws = a['output']
+        return of13.action.output(**output_kws)
+
+    elif typ == pb2.OFPAT_POP_VLAN:
+        return of13.action.pop_vlan()
+
+    elif typ == pb2.OFPAT_PUSH_VLAN:
+        push_vlan_kws = a['push']
+        return of13.action.push_vlan(**push_vlan_kws)
+
+    elif typ == pb2.OFPAT_SET_FIELD:
+        loxi_field = make_loxi_field(a['set_field']['field'])
+        return of13.action.set_field(loxi_field)
+
+    elif typ == pb2.OFPAT_GROUP:
+        group_kws = a['group']
+        return of13.action.group(**group_kws)
+
+    else:
+        raise NotImplementedError(
+            'Action decoder for action OFPAT_* %d' % typ)
+
+
+def ofp_flow_stats_to_loxi_flow_stats(pb):
+    kw = pb2dict(pb)
+
+    def make_loxi_instruction(inst):
+        type = inst['type']
+        if type == pb2.OFPIT_APPLY_ACTIONS:
+            return of13.instruction.apply_actions(
+                actions=[make_loxi_action(a)
+                         for a in inst['actions']['actions']])
+        elif type == pb2.OFPIT_CLEAR_ACTIONS:
+            return of13.instruction.clear_actions()
+        elif type == pb2.OFPIT_GOTO_TABLE:
+            return of13.instruction.goto_table(
+                table_id=inst['goto_table']['table_id'])
+
+        else:
+            raise NotImplementedError('Instruction type %d' % type)
+
+    kw['match'] = make_loxi_match(kw['match'])
+    kw['instructions'] = [make_loxi_instruction(i) for i in kw['instructions']]
+    del kw['id']
+    return of13.flow_stats_entry(**kw)
+
+
+def ofp_packet_in_to_loxi_packet_in(pb):
+    packet_in = of13.message.packet_in(
+        buffer_id=pb.buffer_id,
+        reason=pb.reason,
+        table_id=pb.table_id,
+        cookie=pb.cookie,
+        match=make_loxi_match(pb2dict(pb.match)),
+        data=pb.data
+    )
+    return packet_in
+
+def ofp_group_desc_to_loxi_group_desc(pb):
+    return of13.group_desc_stats_entry(
+        group_type=pb.type,
+        group_id=pb.group_id,
+        buckets=[to_loxi(bucket) for bucket in pb.buckets])
+
+
+def ofp_group_stats_to_loxi_group_stats(pb):
+    return of13.group_stats_entry(
+        group_id=pb.group_id,
+        ref_count=pb.ref_count,
+        packet_count=pb.packet_count,
+        byte_count=pb.byte_count,
+        duration_sec=pb.duration_sec,
+        duration_nsec=pb.duration_nsec,
+        bucket_stats=[to_loxi(bstat) for bstat in pb.bucket_stats])
+
+
+def ofp_bucket_counter_to_loxy_bucket_counter(pb):
+    return of13.bucket_counter(
+        packet_count=pb.packet_count,
+        byte_count=pb.byte_count)
+
+
+def ofp_bucket_to_loxi_bucket(pb):
+    return of13.bucket(
+        weight=pb.weight,
+        watch_port=pb.watch_port,
+        watch_group=pb.watch_group,
+        actions=[to_loxi(action) for action in pb.actions]
+    )
+
+
+to_loxi_converters = {
+    'ofp_port': ofp_port_to_loxi_port_desc,
+    'ofp_port_status': ofp_port_status_to_loxi_port_status,
+    'ofp_flow_stats': ofp_flow_stats_to_loxi_flow_stats,
+    'ofp_packet_in': ofp_packet_in_to_loxi_packet_in,
+    'ofp_group_stats': ofp_group_stats_to_loxi_group_stats,
+    'ofp_group_desc': ofp_group_desc_to_loxi_group_desc,
+    'ofp_bucket_counter': ofp_bucket_counter_to_loxy_bucket_counter,
+    'ofp_bucket': ofp_bucket_to_loxi_bucket,
+    'ofp_action': make_loxi_action,
+    'ofp_port_stats': ofp_port_stats_to_loxi_port_stats
+}
+
+
+def loxi_flow_mod_to_ofp_flow_mod(lo):
+    return pb2.ofp_flow_mod(
+        cookie=lo.cookie,
+        cookie_mask=lo.cookie_mask,
+        table_id=lo.table_id,
+        command=lo._command,
+        idle_timeout=lo.idle_timeout,
+        hard_timeout=lo.hard_timeout,
+        priority=lo.priority,
+        buffer_id=lo.buffer_id,
+        out_port=lo.out_port,
+        out_group=lo.out_group,
+        flags=lo.flags,
+        match=to_grpc(lo.match),
+        instructions=[to_grpc(i) for i in lo.instructions])
+
+
+def loxi_group_mod_to_ofp_group_mod(lo):
+    return pb2.ofp_group_mod(
+        command=lo.command,
+        type=lo.group_type,
+        group_id=lo.group_id,
+        buckets=[to_grpc(b) for b in lo.buckets])
+
+
+def loxi_packet_out_to_ofp_packet_out(lo):
+    return pb2.ofp_packet_out(
+        buffer_id=lo.buffer_id,
+        in_port=lo.in_port,
+        actions=[to_grpc(a) for a in lo.actions],
+        data=lo.data)
+
+
+def loxi_match_v3_to_ofp_match(lo):
+    return pb2.ofp_match(
+        type=pb2.OFPMT_OXM,
+        oxm_fields=[to_grpc(f) for f in lo.oxm_list])
+
+
+def loxi_bucket_to_ofp_bucket(lo):
+    return pb2.ofp_bucket(
+        weight=lo.weight,
+        watch_port=lo.watch_port,
+        watch_group=lo.watch_group,
+        actions=[to_grpc(a) for a in lo.actions])
+
+
+def loxi_oxm_eth_type_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_ETH_TYPE,
+            eth_type=lo.value))
+
+
+def loxi_oxm_in_port_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_IN_PORT,
+            port=lo.value))
+
+
+def loxi_oxm_ip_proto_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_IP_PROTO,
+            ip_proto=lo.value))
+
+
+def loxi_oxm_vlan_vid_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_VLAN_VID,
+            vlan_vid=lo.value))
+
+
+def loxi_oxm_vlan_pcp_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_VLAN_PCP,
+            vlan_pcp=lo.value))
+
+
+def loxi_oxm_ipv4_dst_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_IPV4_DST,
+            ipv4_dst=lo.value))
+
+
+def loxi_oxm_udp_dst_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_UDP_DST,
+            udp_dst=lo.value))
+
+
+def loxi_oxm_udp_src_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_UDP_SRC,
+            udp_src=lo.value))
+
+
+def loxi_oxm_metadata_to_ofp_oxm(lo):
+    return pb2.ofp_oxm_field(
+        oxm_class=pb2.OFPXMC_OPENFLOW_BASIC,
+        ofb_field=pb2.ofp_oxm_ofb_field(
+            type=pb2.OFPXMT_OFB_METADATA,
+            table_metadata=lo.value))
+
+
+def loxi_apply_actions_to_ofp_instruction(lo):
+    return pb2.ofp_instruction(
+        type=pb2.OFPIT_APPLY_ACTIONS,
+        actions=pb2.ofp_instruction_actions(
+            actions=[to_grpc(a) for a in lo.actions]))
+
+def loxi_clear_actions_to_ofp_instruction(lo):
+    return pb2.ofp_instruction(
+        type=pb2.OFPIT_CLEAR_ACTIONS)
+
+
+def loxi_goto_table_to_ofp_instruction(lo):
+    return pb2.ofp_instruction(
+        type=pb2.OFPIT_GOTO_TABLE,
+        goto_table=pb2.ofp_instruction_goto_table(table_id=lo.table_id))
+
+
+def loxi_output_action_to_ofp_action(lo):
+    return pb2.ofp_action(
+        type=pb2.OFPAT_OUTPUT,
+        output=pb2.ofp_action_output(port=lo.port, max_len=lo.max_len))
+
+
+def loxi_group_action_to_ofp_action(lo):
+    return pb2.ofp_action(
+        type=pb2.OFPAT_GROUP,
+        group=pb2.ofp_action_group(group_id=lo.group_id))
+
+
+def loxi_set_field_action_to_ofp_action(lo):
+    return pb2.ofp_action(
+        type=pb2.OFPAT_SET_FIELD,
+        set_field=pb2.ofp_action_set_field(field=to_grpc(lo.field)))
+
+
+def loxi_pop_vlan_action_to_ofp_action(lo):
+    return pb2.ofp_action(type=pb2.OFPAT_POP_VLAN)
+
+
+def loxi_push_vlan_action_to_ofp_action(lo):
+    return pb2.ofp_action(
+        type=pb2.OFPAT_PUSH_VLAN,
+        push=pb2.ofp_action_push(ethertype=lo.ethertype))
+
+
+to_grpc_converters = {
+
+    of13.message.flow_add: loxi_flow_mod_to_ofp_flow_mod,
+    of13.message.flow_delete: loxi_flow_mod_to_ofp_flow_mod,
+    of13.message.flow_delete_strict: loxi_flow_mod_to_ofp_flow_mod,
+    of13.message.flow_modify: loxi_flow_mod_to_ofp_flow_mod,
+    of13.message.flow_modify_strict: loxi_flow_mod_to_ofp_flow_mod,
+
+    of13.message.group_add: loxi_group_mod_to_ofp_group_mod,
+    of13.message.group_delete: loxi_group_mod_to_ofp_group_mod,
+    of13.message.group_modify: loxi_group_mod_to_ofp_group_mod,
+    of13.message.packet_out: loxi_packet_out_to_ofp_packet_out,
+
+    of13.common.match_v3: loxi_match_v3_to_ofp_match,
+    of13.common.bucket: loxi_bucket_to_ofp_bucket,
+
+    of13.oxm.eth_type: loxi_oxm_eth_type_to_ofp_oxm,
+    of13.oxm.in_port: loxi_oxm_in_port_to_ofp_oxm,
+    of13.oxm.ip_proto: loxi_oxm_ip_proto_to_ofp_oxm,
+    of13.oxm.vlan_vid: loxi_oxm_vlan_vid_to_ofp_oxm,
+    of13.oxm.vlan_pcp: loxi_oxm_vlan_pcp_to_ofp_oxm,
+    of13.oxm.ipv4_dst: loxi_oxm_ipv4_dst_to_ofp_oxm,
+    of13.oxm.udp_src: loxi_oxm_udp_src_to_ofp_oxm,
+    of13.oxm.udp_dst: loxi_oxm_udp_dst_to_ofp_oxm,
+    of13.oxm.metadata: loxi_oxm_metadata_to_ofp_oxm,
+
+    of13.instruction.apply_actions: loxi_apply_actions_to_ofp_instruction,
+    of13.instruction.clear_actions: loxi_clear_actions_to_ofp_instruction,
+    of13.instruction.goto_table: loxi_goto_table_to_ofp_instruction,
+
+    of13.action.output: loxi_output_action_to_ofp_action,
+    of13.action.group: loxi_group_action_to_ofp_action,
+    of13.action.set_field: loxi_set_field_action_to_ofp_action,
+    of13.action.pop_vlan: loxi_pop_vlan_action_to_ofp_action,
+    of13.action.push_vlan: loxi_push_vlan_action_to_ofp_action,
+}
diff --git a/python/ofagent/grpc_client.py b/python/ofagent/grpc_client.py
new file mode 100755
index 0000000..5a05696
--- /dev/null
+++ b/python/ofagent/grpc_client.py
@@ -0,0 +1,247 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+The gRPC client layer for the OpenFlow agent
+"""
+from Queue import Queue, Empty
+import os
+
+from grpc import StatusCode
+from grpc._channel import _Rendezvous
+from structlog import get_logger
+from twisted.internet import reactor
+from twisted.internet import threads
+from twisted.internet.defer import inlineCallbacks, returnValue, DeferredQueue
+
+from protos.voltha_pb2 import ID, VolthaServiceStub, FlowTableUpdate, \
+    FlowGroupTableUpdate, PacketOut
+from protos.logical_device_pb2 import LogicalPortId
+from google.protobuf import empty_pb2
+
+
+log = get_logger()
+
+
+class GrpcClient(object):
+
+    def __init__(self, connection_manager, channel, grpc_timeout):
+
+        self.connection_manager = connection_manager
+        self.channel = channel
+        self.grpc_timeout = grpc_timeout
+        self.local_stub = VolthaServiceStub(channel)
+
+        self.stopped = False
+
+        self.packet_out_queue = Queue()  # queue to send out PacketOut msgs
+        self.packet_in_queue = DeferredQueue()  # queue to receive PacketIn
+        self.change_event_queue = DeferredQueue()  # queue change events
+
+    def start(self):
+        log.debug('starting', grpc_timeout=self.grpc_timeout)
+        self.start_packet_out_stream()
+        self.start_packet_in_stream()
+        self.start_change_event_in_stream()
+        reactor.callLater(0, self.packet_in_forwarder_loop)
+        reactor.callLater(0, self.change_event_processing_loop)
+        log.info('started')
+        return self
+
+    def stop(self):
+        log.debug('stopping')
+        self.stopped = True
+        log.info('stopped')
+
+    def start_packet_out_stream(self):
+
+        def packet_generator():
+            while 1:
+                try:
+                    packet = self.packet_out_queue.get(block=True, timeout=1.0)
+                except Empty:
+                    if self.stopped:
+                        return
+                else:
+                    yield packet
+
+        def stream_packets_out():
+            generator = packet_generator()
+            try:
+                self.local_stub.StreamPacketsOut(generator)
+            except _Rendezvous, e:
+                log.error('grpc-exception', status=e.code())
+                if e.code() == StatusCode.UNAVAILABLE:
+                    os.system("kill -15 {}".format(os.getpid()))
+
+        reactor.callInThread(stream_packets_out)
+
+    def start_packet_in_stream(self):
+
+        def receive_packet_in_stream():
+            streaming_rpc_method = self.local_stub.ReceivePacketsIn
+            iterator = streaming_rpc_method(empty_pb2.Empty())
+            try:
+                for packet_in in iterator:
+                    reactor.callFromThread(self.packet_in_queue.put,
+                                           packet_in)
+                    log.debug('enqued-packet-in',
+                              packet_in=packet_in,
+                              queue_len=len(self.packet_in_queue.pending))
+            except _Rendezvous, e:
+                log.error('grpc-exception', status=e.code())
+                if e.code() == StatusCode.UNAVAILABLE:
+                    os.system("kill -15 {}".format(os.getpid()))
+
+        reactor.callInThread(receive_packet_in_stream)
+
+    def start_change_event_in_stream(self):
+
+        def receive_change_events():
+            streaming_rpc_method = self.local_stub.ReceiveChangeEvents
+            iterator = streaming_rpc_method(empty_pb2.Empty())
+            try:
+                for event in iterator:
+                    reactor.callFromThread(self.change_event_queue.put, event)
+                    log.debug('enqued-change-event',
+                              change_event=event,
+                              queue_len=len(self.change_event_queue.pending))
+            except _Rendezvous, e:
+                log.error('grpc-exception', status=e.code())
+                if e.code() == StatusCode.UNAVAILABLE:
+                    os.system("kill -15 {}".format(os.getpid()))
+
+        reactor.callInThread(receive_change_events)
+
+    @inlineCallbacks
+    def change_event_processing_loop(self):
+        while True:
+            try:
+                event = yield self.change_event_queue.get()
+                device_id = event.id
+                self.connection_manager.forward_change_event(device_id, event)
+            except Exception, e:
+                log.exception('failed-in-packet-in-handler', e=e)
+            if self.stopped:
+                break
+
+    @inlineCallbacks
+    def packet_in_forwarder_loop(self):
+        while True:
+            packet_in = yield self.packet_in_queue.get()
+            device_id = packet_in.id
+            ofp_packet_in = packet_in.packet_in
+            self.connection_manager.forward_packet_in(device_id, ofp_packet_in)
+            if self.stopped:
+                break
+
+    def send_packet_out(self, device_id, packet_out):
+        packet_out = PacketOut(id=device_id, packet_out=packet_out)
+        self.packet_out_queue.put(packet_out)
+
+    @inlineCallbacks
+    def get_port(self, device_id, port_id):
+        req = LogicalPortId(id=device_id, port_id=port_id)
+        res = yield threads.deferToThread(
+            self.local_stub.GetLogicalDevicePort, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def get_port_list(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDevicePorts, req, timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
+    def enable_port(self, device_id, port_id):
+        req = LogicalPortId(
+            id=device_id,
+            port_id=port_id
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.EnableLogicalDevicePort, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def disable_port(self, device_id, port_id):
+        req = LogicalPortId(
+            id=device_id,
+            port_id=port_id
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.DisableLogicalDevicePort, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def get_device_info(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.GetLogicalDevice, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def update_flow_table(self, device_id, flow_mod):
+        req = FlowTableUpdate(
+            id=device_id,
+            flow_mod=flow_mod
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.UpdateLogicalDeviceFlowTable, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def update_group_table(self, device_id, group_mod):
+        req = FlowGroupTableUpdate(
+            id=device_id,
+            group_mod=group_mod
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.UpdateLogicalDeviceFlowGroupTable, req, timeout=self.grpc_timeout)
+        returnValue(res)
+
+    @inlineCallbacks
+    def list_flows(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDeviceFlows, req, timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
+    def list_groups(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDeviceFlowGroups, req, timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
+    def list_ports(self, device_id):
+        req = ID(id=device_id)
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDevicePorts, req, timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
+    def list_logical_devices(self):
+        res = yield threads.deferToThread(
+            self.local_stub.ListLogicalDevices, empty_pb2.Empty(), timeout=self.grpc_timeout)
+        returnValue(res.items)
+
+    @inlineCallbacks
+    def subscribe(self, subscriber):
+        res = yield threads.deferToThread(
+            self.local_stub.Subscribe, subscriber, timeout=self.grpc_timeout)
+        returnValue(res)
diff --git a/python/ofagent/loxi/__init__.py b/python/ofagent/loxi/__init__.py
new file mode 100644
index 0000000..adb067b
--- /dev/null
+++ b/python/ofagent/loxi/__init__.py
@@ -0,0 +1,82 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+# Automatically generated by LOXI from template toplevel_init.py
+# Do not modify
+
+version_names = {
+    1: "1.0",
+    2: "1.1",
+    3: "1.2",
+    4: "1.3",
+    5: "1.4",
+}
+
+def protocol(ver):
+    """
+    Import and return the protocol module for the given wire version.
+    """
+    if ver == 1:
+        import of10
+        return of10
+
+    if ver == 2:
+        import of11
+        return of11
+
+    if ver == 3:
+        import of12
+        return of12
+
+    if ver == 4:
+        import of13
+        return of13
+
+    if ver == 5:
+        import of14
+        return of14
+
+    raise ValueError
+
+class ProtocolError(Exception):
+    """
+    Raised when failing to deserialize an invalid OpenFlow message.
+    """
+    pass
+
+class Unimplemented(Exception):
+    """
+    Raised when an OpenFlow feature is not yet implemented in PyLoxi.
+    """
+    pass
+
+def unimplemented(msg):
+    raise Unimplemented(msg)
+
+class OFObject(object):
+    """
+    Superclass of all OpenFlow classes
+    """
+    def __init__(self, *args):
+        raise NotImplementedError("cannot instantiate abstract class")
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def show(self):
+        import loxi.pp
+        return loxi.pp.pp(self)
diff --git a/python/ofagent/loxi/connection.py b/python/ofagent/loxi/connection.py
new file mode 100644
index 0000000..fc61161
--- /dev/null
+++ b/python/ofagent/loxi/connection.py
@@ -0,0 +1,263 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright 2015, Big Switch Networks, Inc.
+
+"""
+OpenFlow connection class
+
+This class creates a thread which continually parses OpenFlow messages off the
+supplied socket and places them in a queue. The class has methods for reading messages
+from the RX queue, sending messages, and higher level operations like request-response
+and multipart transactions.
+"""
+
+import loxi
+import loxi.of14
+import logging
+import time
+import socket
+import errno
+import os
+import select
+from threading import Condition, Lock, Thread
+
+DEFAULT_TIMEOUT = 1
+
+class TransactionError(Exception):
+    def __str__(self):
+        return self.args[0]
+
+    @property
+    def msg(self):
+        return self.args[1]
+
+class ConnectionClosed(Exception):
+    pass
+
+class Connection(Thread):
+    def __init__(self, sock):
+        Thread.__init__(self)
+        self.sock = sock
+        self.logger = logging.getLogger("connection")
+        self.rx = []
+        self.rx_cv = Condition()
+        self.tx_lock = Lock()
+        self.next_xid = 1
+        self.wakeup_rd, self.wakeup_wr = os.pipe()
+        self.finished = False
+        self.read_buffer = None
+
+    def start(self):
+        while not self.finished:
+            try:
+                rd, wr, err = select.select([self.sock, self.wakeup_rd], [], [])
+            except select.error:
+                print 'SELECT ERROR!!!'
+            if self.sock in rd:
+                try:
+                    self.process_read()
+                except ConnectionClosed:
+                    self.logger.info("Connection to controller was dropped")
+                    self.finished = True
+            if self.wakeup_rd in rd:
+                os.read(self.wakeup_rd, 1)
+        self.logger.debug("Exited event loop")
+
+    def process_read(self):
+        recvd = self.sock.recv(4096)
+        if (len(recvd)) == 0:
+            # this indicates that the other end hang up
+            raise ConnectionClosed()
+
+        self.logger.debug("Received %d bytes", len(recvd))
+
+        buf = self.read_buffer
+        if buf:
+            buf += recvd
+        else:
+            buf = recvd
+
+        offset = 0
+        while offset < len(buf):
+            if offset + 8 > len(buf):
+                # Not enough data for the OpenFlow header
+                break
+
+            # Parse the header to get type
+            hdr_version, hdr_type, hdr_msglen, hdr_xid = loxi.of14.message.parse_header(buf[offset:])
+
+            # Use loxi to resolve ofp of matching version
+            ofp = loxi.protocol(hdr_version)
+
+            # Extract the raw message bytes
+            if (offset + hdr_msglen) > len(buf):
+                # Not enough data for the body
+                break
+            rawmsg = buf[offset : offset + hdr_msglen]
+            offset += hdr_msglen
+
+            msg = ofp.message.parse_message(rawmsg)
+            if not msg:
+                self.logger.warn("Could not parse message")
+                continue
+
+            self.logger.debug("Received message %s.%s xid %d length %d",
+                              type(msg).__module__, type(msg).__name__, hdr_xid, hdr_msglen)
+
+            with self.rx_cv:
+                self.rx.append(msg)
+                self.rx_cv.notify_all()
+
+        if offset == len(buf):
+            self.read_buffer = None
+        else:
+            self.read_buffer = buf[offset:]
+            self.logger.debug("%d bytes remaining", len(self.read_buffer))
+
+    def recv(self, predicate, timeout=DEFAULT_TIMEOUT):
+        """
+        Remove and return the first message in the RX queue for
+        which 'predicate' returns true
+        """
+        assert self.is_alive()
+
+        deadline = time.time() + timeout
+        while True:
+            with self.rx_cv:
+                for i, msg in enumerate(self.rx):
+                    if predicate(msg):
+                        return self.rx.pop(i)
+
+                now = time.time()
+                if now > deadline:
+                    return None
+                else:
+                    self.rx_cv.wait(deadline - now)
+
+    def recv_any(self, timeout=DEFAULT_TIMEOUT):
+        """
+        Return the first message in the RX queue
+        """
+        return self.recv(lambda msg: True, timeout)
+
+    def recv_xid(self, xid, timeout=DEFAULT_TIMEOUT):
+        """
+        Return the first message in the RX queue with XID 'xid'
+        """
+        return self.recv(lambda msg: msg.xid == xid, timeout)
+
+    def recv_class(self, klass, timeout=DEFAULT_TIMEOUT):
+        """
+        Return the first message in the RX queue which is an instance of 'klass'
+        """
+        return self.recv(lambda msg: isinstance(msg, klass), timeout)
+
+    def send_raw(self, buf):
+        """
+        Send raw bytes on the socket
+        """
+        assert self.is_alive()
+        self.logger.debug("Sending raw message length %d", len(buf))
+        with self.tx_lock:
+            if self.sock.sendall(buf) is not None:
+                raise RuntimeError("failed to send message to switch")
+
+    def send(self, msg):
+        """
+        Send a message
+        """
+        assert self.is_alive()
+
+        if msg.xid is None:
+            msg.xid = self._gen_xid()
+        buf = msg.pack()
+        self.logger.debug("Sending message %s.%s xid %d length %d",
+                          type(msg).__module__, type(msg).__name__, msg.xid, len(buf))
+        with self.tx_lock:
+            if self.sock.sendall(buf) is not None:
+                raise RuntimeError("failed to send message to switch")
+
+    def transact(self, msg, timeout=DEFAULT_TIMEOUT):
+        """
+        Send a message and return the reply
+        """
+        self.send(msg)
+        reply = self.recv_xid(msg.xid, timeout)
+        if reply is None:
+            raise TransactionError("no reply for %s" % type(msg).__name__, None)
+        elif isinstance(reply, loxi.protocol(reply.version).message.error_msg):
+            raise TransactionError("received %s in response to %s" % (type(reply).__name__, type(msg).__name__), reply)
+        return reply
+
+    def transact_multipart_generator(self, msg, timeout=DEFAULT_TIMEOUT):
+        """
+        Send a multipart request and yield each entry from the replies
+        """
+        self.send(msg)
+        finished = False
+        while not finished:
+            reply = self.recv_xid(msg.xid, timeout)
+            if reply is None:
+                raise TransactionError("no reply for %s" % type(msg).__name__, None)
+            elif not isinstance(reply, loxi.protocol(reply.version).message.stats_reply):
+                raise TransactionError("received %s in response to %s" % (type(reply).__name__, type(msg).__name__), reply)
+            for entry in reply.entries:
+                yield entry
+            finished = reply.flags & loxi.protocol(reply.version).OFPSF_REPLY_MORE == 0
+
+    def transact_multipart(self, msg, timeout=DEFAULT_TIMEOUT):
+        """
+        Send a multipart request and return all entries from the replies
+        """
+        entries = []
+        for entry in self.transact_multipart_generator(msg, timeout):
+            entries.append(entry)
+        return entries
+
+    def stop(self):
+        """
+        Signal the thread to exit and wait for it
+        """
+        assert not self.finished
+        self.logger.debug("Stopping connection")
+        self.finished = True
+        os.write(self.wakeup_wr, "x")
+        self.join()
+        self.sock.close()
+        os.close(self.wakeup_rd)
+        os.close(self.wakeup_wr)
+        self.logger.debug("Stopped connection")
+
+    def _gen_xid(self):
+        xid = self.next_xid
+        self.next_xid += 1
+        return xid
+
+def connect(ip, port=6653, daemon=True, ofp=loxi.of14):
+    """
+    Actively connect to a switch
+    """
+    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    soc.connect((ip, port))
+    soc.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
+    cxn = Connection(soc)
+    cxn.daemon = daemon
+    cxn.logger.debug("Connected to %s:%d", ip, port)
+    cxn.start()
+
+    cxn.send(ofp.message.hello())
+    if not cxn.recv(lambda msg: msg.type == ofp.OFPT_HELLO):
+        raise Exception("Did not receive HELLO")
+
+    return cxn
diff --git a/python/ofagent/loxi/generic_util.py b/python/ofagent/loxi/generic_util.py
new file mode 100644
index 0000000..7cbf32e
--- /dev/null
+++ b/python/ofagent/loxi/generic_util.py
@@ -0,0 +1,113 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+"""
+Utility functions independent of the protocol version
+"""
+
+# Automatically generated by LOXI from template generic_util.py
+# Do not modify
+
+import loxi
+import struct
+
+def pack_list(values):
+    return "".join([x.pack() for x in values])
+
+def unpack_list(reader, deserializer):
+    """
+    The deserializer function should take an OFReader and return the new object.
+    """
+    entries = []
+    while not reader.is_empty():
+        entries.append(deserializer(reader))
+    return entries
+
+def pad_to(alignment, length):
+    """
+    Return a string of zero bytes that will pad a string of length 'length' to
+    a multiple of 'alignment'.
+    """
+    return "\x00" * ((length + alignment - 1)/alignment*alignment - length)
+
+class OFReader(object):
+    """
+    Cursor over a read-only buffer
+
+    OpenFlow messages are best thought of as a sequence of elements of
+    variable size, rather than a C-style struct with fixed offsets and
+    known field lengths. This class supports efficiently reading
+    fields sequentially and is intended to be used recursively by the
+    parsers of child objects which will implicitly update the offset.
+
+    buf: buffer object
+    start: initial position in the buffer
+    length: number of bytes after start
+    offset: distance from start
+    """
+    def __init__(self, buf, start=0, length=None):
+        self.buf = buf
+        self.start = start
+        if length is None:
+            self.length = len(buf) - start
+        else:
+            self.length = length
+        self.offset = 0
+
+    def read(self, fmt):
+        st = struct.Struct(fmt)
+        if self.offset + st.size > self.length:
+            raise loxi.ProtocolError("Buffer too short")
+        result = st.unpack_from(self.buf, self.start+self.offset)
+        self.offset += st.size
+        return result
+
+    def read_all(self):
+        s = self.buf[(self.start+self.offset):(self.start+self.length)]
+        assert(len(s) == self.length - self.offset)
+        self.offset = self.length
+        return s
+
+    def peek(self, fmt, offset=0):
+        st = struct.Struct(fmt)
+        if self.offset + offset + st.size > self.length:
+            raise loxi.ProtocolError("Buffer too short")
+        result = st.unpack_from(self.buf, self.start + self.offset + offset)
+        return result
+
+    def skip(self, length):
+        if self.offset + length > self.length:
+            raise loxi.ProtocolError("Buffer too short")
+        self.offset += length
+
+    def skip_align(self):
+        new_offset = (self.offset + 7) / 8 * 8
+        if new_offset > self.length:
+            raise loxi.ProtocolError("Buffer too short")
+        self.offset = new_offset
+
+    def is_empty(self):
+        return self.offset == self.length
+
+    # Used when parsing objects that have their own length fields
+    def slice(self, length, rewind=0):
+        if self.offset + length - rewind > self.length:
+            raise loxi.ProtocolError("Buffer too short")
+        reader = OFReader(self.buf, self.start + self.offset - rewind, length)
+        reader.skip(rewind)
+        self.offset += length - rewind
+        return reader
diff --git a/python/ofagent/loxi/of10/__init__.py b/python/ofagent/loxi/of10/__init__.py
new file mode 100644
index 0000000..d4457f5
--- /dev/null
+++ b/python/ofagent/loxi/of10/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+
+# Automatically generated by LOXI from template init.py
+# Do not modify
+
+import const
+import action
+import message
+import common
+from const import *
+from common import *
+from loxi import ProtocolError
diff --git a/python/ofagent/loxi/of10/action.py b/python/ofagent/loxi/of10/action.py
new file mode 100644
index 0000000..43702f0
--- /dev/null
+++ b/python/ofagent/loxi/of10/action.py
@@ -0,0 +1,1088 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+
+# Automatically generated by LOXI from template module.py
+# Do not modify
+
+import struct
+import loxi
+import util
+import loxi.generic_util
+
+import sys
+ofp = sys.modules['loxi.of10']
+
+class action(loxi.OFObject):
+    subtypes = {}
+
+
+    def __init__(self, type=None):
+        if type != None:
+            self.type = type
+        else:
+            self.type = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 0)
+        subclass = action.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = action()
+        obj.type = reader.read("!H")[0]
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.type != other.type: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("action {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+
+class experimenter(action):
+    subtypes = {}
+
+    type = 65535
+
+    def __init__(self, experimenter=None, data=None):
+        if experimenter != None:
+            self.experimenter = experimenter
+        else:
+            self.experimenter = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 4)
+        subclass = experimenter.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = experimenter()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.experimenter = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.experimenter != other.experimenter: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("experimenter {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[65535] = experimenter
+
+class bsn(experimenter):
+    subtypes = {}
+
+    type = 65535
+    experimenter = 6035143
+
+    def __init__(self, subtype=None):
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 8)
+        subclass = bsn.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = bsn()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        obj.subtype = reader.read("!L")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+experimenter.subtypes[6035143] = bsn
+
+class bsn_checksum(bsn):
+    type = 65535
+    experimenter = 6035143
+    subtype = 4
+
+    def __init__(self, checksum=None):
+        if checksum != None:
+            self.checksum = checksum
+        else:
+            self.checksum = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(util.pack_checksum_128(self.checksum))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_checksum()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 4)
+        obj.checksum = util.unpack_checksum_128(reader)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.checksum != other.checksum: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_checksum {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("checksum = ");
+                q.pp(self.checksum)
+            q.breakable()
+        q.text('}')
+
+bsn.subtypes[4] = bsn_checksum
+
+class bsn_mirror(bsn):
+    type = 65535
+    experimenter = 6035143
+    subtype = 1
+
+    def __init__(self, dest_port=None, vlan_tag=None, copy_stage=None):
+        if dest_port != None:
+            self.dest_port = dest_port
+        else:
+            self.dest_port = 0
+        if vlan_tag != None:
+            self.vlan_tag = vlan_tag
+        else:
+            self.vlan_tag = 0
+        if copy_stage != None:
+            self.copy_stage = copy_stage
+        else:
+            self.copy_stage = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.dest_port))
+        packed.append(struct.pack("!L", self.vlan_tag))
+        packed.append(struct.pack("!B", self.copy_stage))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_mirror()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 1)
+        obj.dest_port = reader.read("!L")[0]
+        obj.vlan_tag = reader.read("!L")[0]
+        obj.copy_stage = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.dest_port != other.dest_port: return False
+        if self.vlan_tag != other.vlan_tag: return False
+        if self.copy_stage != other.copy_stage: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_mirror {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("dest_port = ");
+                q.text("%#x" % self.dest_port)
+                q.text(","); q.breakable()
+                q.text("vlan_tag = ");
+                q.text("%#x" % self.vlan_tag)
+                q.text(","); q.breakable()
+                q.text("copy_stage = ");
+                q.text("%#x" % self.copy_stage)
+            q.breakable()
+        q.text('}')
+
+bsn.subtypes[1] = bsn_mirror
+
+class bsn_set_tunnel_dst(bsn):
+    type = 65535
+    experimenter = 6035143
+    subtype = 2
+
+    def __init__(self, dst=None):
+        if dst != None:
+            self.dst = dst
+        else:
+            self.dst = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.dst))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_tunnel_dst()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 2)
+        obj.dst = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.dst != other.dst: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_tunnel_dst {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("dst = ");
+                q.text("%#x" % self.dst)
+            q.breakable()
+        q.text('}')
+
+bsn.subtypes[2] = bsn_set_tunnel_dst
+
+class enqueue(action):
+    type = 11
+
+    def __init__(self, port=None, queue_id=None):
+        if port != None:
+            self.port = port
+        else:
+            self.port = 0
+        if queue_id != None:
+            self.queue_id = queue_id
+        else:
+            self.queue_id = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(util.pack_port_no(self.port))
+        packed.append('\x00' * 6)
+        packed.append(struct.pack("!L", self.queue_id))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = enqueue()
+        _type = reader.read("!H")[0]
+        assert(_type == 11)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.port = util.unpack_port_no(reader)
+        reader.skip(6)
+        obj.queue_id = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port != other.port: return False
+        if self.queue_id != other.queue_id: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("enqueue {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port = ");
+                q.text(util.pretty_port(self.port))
+                q.text(","); q.breakable()
+                q.text("queue_id = ");
+                q.text("%#x" % self.queue_id)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[11] = enqueue
+
+class nicira(experimenter):
+    subtypes = {}
+
+    type = 65535
+    experimenter = 8992
+
+    def __init__(self, subtype=None):
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!H", self.subtype))
+        packed.append('\x00' * 2)
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 8)
+        subclass = nicira.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = nicira()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 8992)
+        obj.subtype = reader.read("!H")[0]
+        reader.skip(2)
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("nicira {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+experimenter.subtypes[8992] = nicira
+
+class nicira_dec_ttl(nicira):
+    type = 65535
+    experimenter = 8992
+    subtype = 18
+
+    def __init__(self):
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!H", self.subtype))
+        packed.append('\x00' * 2)
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = nicira_dec_ttl()
+        _type = reader.read("!H")[0]
+        assert(_type == 65535)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 8992)
+        _subtype = reader.read("!H")[0]
+        assert(_subtype == 18)
+        reader.skip(2)
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("nicira_dec_ttl {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+nicira.subtypes[18] = nicira_dec_ttl
+
+class output(action):
+    type = 0
+
+    def __init__(self, port=None, max_len=None):
+        if port != None:
+            self.port = port
+        else:
+            self.port = 0
+        if max_len != None:
+            self.max_len = max_len
+        else:
+            self.max_len = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(util.pack_port_no(self.port))
+        packed.append(struct.pack("!H", self.max_len))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = output()
+        _type = reader.read("!H")[0]
+        assert(_type == 0)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.port = util.unpack_port_no(reader)
+        obj.max_len = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port != other.port: return False
+        if self.max_len != other.max_len: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("output {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port = ");
+                q.text(util.pretty_port(self.port))
+                q.text(","); q.breakable()
+                q.text("max_len = ");
+                q.text("%#x" % self.max_len)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[0] = output
+
+class set_dl_dst(action):
+    type = 5
+
+    def __init__(self, dl_addr=None):
+        if dl_addr != None:
+            self.dl_addr = dl_addr
+        else:
+            self.dl_addr = [0,0,0,0,0,0]
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!6B", *self.dl_addr))
+        packed.append('\x00' * 6)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_dl_dst()
+        _type = reader.read("!H")[0]
+        assert(_type == 5)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.dl_addr = list(reader.read('!6B'))
+        reader.skip(6)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.dl_addr != other.dl_addr: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_dl_dst {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("dl_addr = ");
+                q.text(util.pretty_mac(self.dl_addr))
+            q.breakable()
+        q.text('}')
+
+action.subtypes[5] = set_dl_dst
+
+class set_dl_src(action):
+    type = 4
+
+    def __init__(self, dl_addr=None):
+        if dl_addr != None:
+            self.dl_addr = dl_addr
+        else:
+            self.dl_addr = [0,0,0,0,0,0]
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!6B", *self.dl_addr))
+        packed.append('\x00' * 6)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_dl_src()
+        _type = reader.read("!H")[0]
+        assert(_type == 4)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.dl_addr = list(reader.read('!6B'))
+        reader.skip(6)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.dl_addr != other.dl_addr: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_dl_src {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("dl_addr = ");
+                q.text(util.pretty_mac(self.dl_addr))
+            q.breakable()
+        q.text('}')
+
+action.subtypes[4] = set_dl_src
+
+class set_nw_dst(action):
+    type = 7
+
+    def __init__(self, nw_addr=None):
+        if nw_addr != None:
+            self.nw_addr = nw_addr
+        else:
+            self.nw_addr = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.nw_addr))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_nw_dst()
+        _type = reader.read("!H")[0]
+        assert(_type == 7)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.nw_addr = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.nw_addr != other.nw_addr: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_nw_dst {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("nw_addr = ");
+                q.text("%#x" % self.nw_addr)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[7] = set_nw_dst
+
+class set_nw_src(action):
+    type = 6
+
+    def __init__(self, nw_addr=None):
+        if nw_addr != None:
+            self.nw_addr = nw_addr
+        else:
+            self.nw_addr = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!L", self.nw_addr))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_nw_src()
+        _type = reader.read("!H")[0]
+        assert(_type == 6)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.nw_addr = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.nw_addr != other.nw_addr: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_nw_src {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("nw_addr = ");
+                q.text("%#x" % self.nw_addr)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[6] = set_nw_src
+
+class set_nw_tos(action):
+    type = 8
+
+    def __init__(self, nw_tos=None):
+        if nw_tos != None:
+            self.nw_tos = nw_tos
+        else:
+            self.nw_tos = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!B", self.nw_tos))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_nw_tos()
+        _type = reader.read("!H")[0]
+        assert(_type == 8)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.nw_tos = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.nw_tos != other.nw_tos: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_nw_tos {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("nw_tos = ");
+                q.text("%#x" % self.nw_tos)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[8] = set_nw_tos
+
+class set_tp_dst(action):
+    type = 10
+
+    def __init__(self, tp_port=None):
+        if tp_port != None:
+            self.tp_port = tp_port
+        else:
+            self.tp_port = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!H", self.tp_port))
+        packed.append('\x00' * 2)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_tp_dst()
+        _type = reader.read("!H")[0]
+        assert(_type == 10)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.tp_port = reader.read("!H")[0]
+        reader.skip(2)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.tp_port != other.tp_port: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_tp_dst {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("tp_port = ");
+                q.text("%#x" % self.tp_port)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[10] = set_tp_dst
+
+class set_tp_src(action):
+    type = 9
+
+    def __init__(self, tp_port=None):
+        if tp_port != None:
+            self.tp_port = tp_port
+        else:
+            self.tp_port = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!H", self.tp_port))
+        packed.append('\x00' * 2)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_tp_src()
+        _type = reader.read("!H")[0]
+        assert(_type == 9)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.tp_port = reader.read("!H")[0]
+        reader.skip(2)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.tp_port != other.tp_port: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_tp_src {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("tp_port = ");
+                q.text("%#x" % self.tp_port)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[9] = set_tp_src
+
+class set_vlan_pcp(action):
+    type = 2
+
+    def __init__(self, vlan_pcp=None):
+        if vlan_pcp != None:
+            self.vlan_pcp = vlan_pcp
+        else:
+            self.vlan_pcp = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!B", self.vlan_pcp))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_vlan_pcp()
+        _type = reader.read("!H")[0]
+        assert(_type == 2)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.vlan_pcp = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.vlan_pcp != other.vlan_pcp: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_vlan_pcp {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("vlan_pcp = ");
+                q.text("%#x" % self.vlan_pcp)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[2] = set_vlan_pcp
+
+class set_vlan_vid(action):
+    type = 1
+
+    def __init__(self, vlan_vid=None):
+        if vlan_vid != None:
+            self.vlan_vid = vlan_vid
+        else:
+            self.vlan_vid = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append(struct.pack("!H", self.vlan_vid))
+        packed.append('\x00' * 2)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = set_vlan_vid()
+        _type = reader.read("!H")[0]
+        assert(_type == 1)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        obj.vlan_vid = reader.read("!H")[0]
+        reader.skip(2)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.vlan_vid != other.vlan_vid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("set_vlan_vid {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("vlan_vid = ");
+                q.text("%#x" % self.vlan_vid)
+            q.breakable()
+        q.text('}')
+
+action.subtypes[1] = set_vlan_vid
+
+class strip_vlan(action):
+    type = 3
+
+    def __init__(self):
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = strip_vlan()
+        _type = reader.read("!H")[0]
+        assert(_type == 3)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("strip_vlan {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+action.subtypes[3] = strip_vlan
+
+
diff --git a/python/ofagent/loxi/of10/common.py b/python/ofagent/loxi/of10/common.py
new file mode 100644
index 0000000..e7bd77d
--- /dev/null
+++ b/python/ofagent/loxi/of10/common.py
@@ -0,0 +1,1312 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+
+# Automatically generated by LOXI from template module.py
+# Do not modify
+
+import struct
+import loxi
+import util
+import loxi.generic_util
+
+import sys
+ofp = sys.modules['loxi.of10']
+
+class bsn_interface(loxi.OFObject):
+
+    def __init__(self, hw_addr=None, name=None, ipv4_addr=None, ipv4_netmask=None):
+        if hw_addr != None:
+            self.hw_addr = hw_addr
+        else:
+            self.hw_addr = [0,0,0,0,0,0]
+        if name != None:
+            self.name = name
+        else:
+            self.name = ""
+        if ipv4_addr != None:
+            self.ipv4_addr = ipv4_addr
+        else:
+            self.ipv4_addr = 0
+        if ipv4_netmask != None:
+            self.ipv4_netmask = ipv4_netmask
+        else:
+            self.ipv4_netmask = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!6B", *self.hw_addr))
+        packed.append('\x00' * 2)
+        packed.append(struct.pack("!16s", self.name))
+        packed.append(struct.pack("!L", self.ipv4_addr))
+        packed.append(struct.pack("!L", self.ipv4_netmask))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_interface()
+        obj.hw_addr = list(reader.read('!6B'))
+        reader.skip(2)
+        obj.name = reader.read("!16s")[0].rstrip("\x00")
+        obj.ipv4_addr = reader.read("!L")[0]
+        obj.ipv4_netmask = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.hw_addr != other.hw_addr: return False
+        if self.name != other.name: return False
+        if self.ipv4_addr != other.ipv4_addr: return False
+        if self.ipv4_netmask != other.ipv4_netmask: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_interface {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("hw_addr = ");
+                q.text(util.pretty_mac(self.hw_addr))
+                q.text(","); q.breakable()
+                q.text("name = ");
+                q.pp(self.name)
+                q.text(","); q.breakable()
+                q.text("ipv4_addr = ");
+                q.text(util.pretty_ipv4(self.ipv4_addr))
+                q.text(","); q.breakable()
+                q.text("ipv4_netmask = ");
+                q.text(util.pretty_ipv4(self.ipv4_netmask))
+            q.breakable()
+        q.text('}')
+
+
+class bsn_vport(loxi.OFObject):
+    subtypes = {}
+
+
+    def __init__(self, type=None):
+        if type != None:
+            self.type = type
+        else:
+            self.type = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 1
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 0)
+        subclass = bsn_vport.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = bsn_vport()
+        obj.type = reader.read("!H")[0]
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.type != other.type: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_vport {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+
+class bsn_vport_l2gre(bsn_vport):
+    type = 1
+
+    def __init__(self, flags=None, port_no=None, loopback_port_no=None, local_mac=None, nh_mac=None, src_ip=None, dst_ip=None, dscp=None, ttl=None, vpn=None, rate_limit=None, if_name=None):
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if loopback_port_no != None:
+            self.loopback_port_no = loopback_port_no
+        else:
+            self.loopback_port_no = 0
+        if local_mac != None:
+            self.local_mac = local_mac
+        else:
+            self.local_mac = [0,0,0,0,0,0]
+        if nh_mac != None:
+            self.nh_mac = nh_mac
+        else:
+            self.nh_mac = [0,0,0,0,0,0]
+        if src_ip != None:
+            self.src_ip = src_ip
+        else:
+            self.src_ip = 0
+        if dst_ip != None:
+            self.dst_ip = dst_ip
+        else:
+            self.dst_ip = 0
+        if dscp != None:
+            self.dscp = dscp
+        else:
+            self.dscp = 0
+        if ttl != None:
+            self.ttl = ttl
+        else:
+            self.ttl = 0
+        if vpn != None:
+            self.vpn = vpn
+        else:
+            self.vpn = 0
+        if rate_limit != None:
+            self.rate_limit = rate_limit
+        else:
+            self.rate_limit = 0
+        if if_name != None:
+            self.if_name = if_name
+        else:
+            self.if_name = ""
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 1
+        packed.append(struct.pack("!L", self.flags))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(util.pack_port_no(self.loopback_port_no))
+        packed.append(struct.pack("!6B", *self.local_mac))
+        packed.append(struct.pack("!6B", *self.nh_mac))
+        packed.append(struct.pack("!L", self.src_ip))
+        packed.append(struct.pack("!L", self.dst_ip))
+        packed.append(struct.pack("!B", self.dscp))
+        packed.append(struct.pack("!B", self.ttl))
+        packed.append('\x00' * 2)
+        packed.append(struct.pack("!L", self.vpn))
+        packed.append(struct.pack("!L", self.rate_limit))
+        packed.append(struct.pack("!16s", self.if_name))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_vport_l2gre()
+        _type = reader.read("!H")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.flags = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.loopback_port_no = util.unpack_port_no(reader)
+        obj.local_mac = list(reader.read('!6B'))
+        obj.nh_mac = list(reader.read('!6B'))
+        obj.src_ip = reader.read("!L")[0]
+        obj.dst_ip = reader.read("!L")[0]
+        obj.dscp = reader.read("!B")[0]
+        obj.ttl = reader.read("!B")[0]
+        reader.skip(2)
+        obj.vpn = reader.read("!L")[0]
+        obj.rate_limit = reader.read("!L")[0]
+        obj.if_name = reader.read("!16s")[0].rstrip("\x00")
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.flags != other.flags: return False
+        if self.port_no != other.port_no: return False
+        if self.loopback_port_no != other.loopback_port_no: return False
+        if self.local_mac != other.local_mac: return False
+        if self.nh_mac != other.nh_mac: return False
+        if self.src_ip != other.src_ip: return False
+        if self.dst_ip != other.dst_ip: return False
+        if self.dscp != other.dscp: return False
+        if self.ttl != other.ttl: return False
+        if self.vpn != other.vpn: return False
+        if self.rate_limit != other.rate_limit: return False
+        if self.if_name != other.if_name: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_vport_l2gre {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("loopback_port_no = ");
+                q.text(util.pretty_port(self.loopback_port_no))
+                q.text(","); q.breakable()
+                q.text("local_mac = ");
+                q.text(util.pretty_mac(self.local_mac))
+                q.text(","); q.breakable()
+                q.text("nh_mac = ");
+                q.text(util.pretty_mac(self.nh_mac))
+                q.text(","); q.breakable()
+                q.text("src_ip = ");
+                q.text(util.pretty_ipv4(self.src_ip))
+                q.text(","); q.breakable()
+                q.text("dst_ip = ");
+                q.text(util.pretty_ipv4(self.dst_ip))
+                q.text(","); q.breakable()
+                q.text("dscp = ");
+                q.text("%#x" % self.dscp)
+                q.text(","); q.breakable()
+                q.text("ttl = ");
+                q.text("%#x" % self.ttl)
+                q.text(","); q.breakable()
+                q.text("vpn = ");
+                q.text("%#x" % self.vpn)
+                q.text(","); q.breakable()
+                q.text("rate_limit = ");
+                q.text("%#x" % self.rate_limit)
+                q.text(","); q.breakable()
+                q.text("if_name = ");
+                q.pp(self.if_name)
+            q.breakable()
+        q.text('}')
+
+bsn_vport.subtypes[1] = bsn_vport_l2gre
+
+class bsn_vport_q_in_q(bsn_vport):
+    type = 0
+
+    def __init__(self, port_no=None, ingress_tpid=None, ingress_vlan_id=None, egress_tpid=None, egress_vlan_id=None, if_name=None):
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if ingress_tpid != None:
+            self.ingress_tpid = ingress_tpid
+        else:
+            self.ingress_tpid = 0
+        if ingress_vlan_id != None:
+            self.ingress_vlan_id = ingress_vlan_id
+        else:
+            self.ingress_vlan_id = 0
+        if egress_tpid != None:
+            self.egress_tpid = egress_tpid
+        else:
+            self.egress_tpid = 0
+        if egress_vlan_id != None:
+            self.egress_vlan_id = egress_vlan_id
+        else:
+            self.egress_vlan_id = 0
+        if if_name != None:
+            self.if_name = if_name
+        else:
+            self.if_name = ""
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 1
+        packed.append(struct.pack("!L", self.port_no))
+        packed.append(struct.pack("!H", self.ingress_tpid))
+        packed.append(struct.pack("!H", self.ingress_vlan_id))
+        packed.append(struct.pack("!H", self.egress_tpid))
+        packed.append(struct.pack("!H", self.egress_vlan_id))
+        packed.append(struct.pack("!16s", self.if_name))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_vport_q_in_q()
+        _type = reader.read("!H")[0]
+        assert(_type == 0)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.port_no = reader.read("!L")[0]
+        obj.ingress_tpid = reader.read("!H")[0]
+        obj.ingress_vlan_id = reader.read("!H")[0]
+        obj.egress_tpid = reader.read("!H")[0]
+        obj.egress_vlan_id = reader.read("!H")[0]
+        obj.if_name = reader.read("!16s")[0].rstrip("\x00")
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port_no != other.port_no: return False
+        if self.ingress_tpid != other.ingress_tpid: return False
+        if self.ingress_vlan_id != other.ingress_vlan_id: return False
+        if self.egress_tpid != other.egress_tpid: return False
+        if self.egress_vlan_id != other.egress_vlan_id: return False
+        if self.if_name != other.if_name: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_vport_q_in_q {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port_no = ");
+                q.text("%#x" % self.port_no)
+                q.text(","); q.breakable()
+                q.text("ingress_tpid = ");
+                q.text("%#x" % self.ingress_tpid)
+                q.text(","); q.breakable()
+                q.text("ingress_vlan_id = ");
+                q.text("%#x" % self.ingress_vlan_id)
+                q.text(","); q.breakable()
+                q.text("egress_tpid = ");
+                q.text("%#x" % self.egress_tpid)
+                q.text(","); q.breakable()
+                q.text("egress_vlan_id = ");
+                q.text("%#x" % self.egress_vlan_id)
+                q.text(","); q.breakable()
+                q.text("if_name = ");
+                q.pp(self.if_name)
+            q.breakable()
+        q.text('}')
+
+bsn_vport.subtypes[0] = bsn_vport_q_in_q
+
+class flow_stats_entry(loxi.OFObject):
+
+    def __init__(self, table_id=None, match=None, duration_sec=None, duration_nsec=None, priority=None, idle_timeout=None, hard_timeout=None, cookie=None, packet_count=None, byte_count=None, actions=None):
+        if table_id != None:
+            self.table_id = table_id
+        else:
+            self.table_id = 0
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if duration_sec != None:
+            self.duration_sec = duration_sec
+        else:
+            self.duration_sec = 0
+        if duration_nsec != None:
+            self.duration_nsec = duration_nsec
+        else:
+            self.duration_nsec = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if packet_count != None:
+            self.packet_count = packet_count
+        else:
+            self.packet_count = 0
+        if byte_count != None:
+            self.byte_count = byte_count
+        else:
+            self.byte_count = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 0
+        packed.append(struct.pack("!B", self.table_id))
+        packed.append('\x00' * 1)
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!L", self.duration_sec))
+        packed.append(struct.pack("!L", self.duration_nsec))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append('\x00' * 6)
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(struct.pack("!Q", self.packet_count))
+        packed.append(struct.pack("!Q", self.byte_count))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[0] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_stats_entry()
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 2)
+        obj.table_id = reader.read("!B")[0]
+        reader.skip(1)
+        obj.match = ofp.match.unpack(reader)
+        obj.duration_sec = reader.read("!L")[0]
+        obj.duration_nsec = reader.read("!L")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        reader.skip(6)
+        obj.cookie = reader.read("!Q")[0]
+        obj.packet_count = reader.read("!Q")[0]
+        obj.byte_count = reader.read("!Q")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.table_id != other.table_id: return False
+        if self.match != other.match: return False
+        if self.duration_sec != other.duration_sec: return False
+        if self.duration_nsec != other.duration_nsec: return False
+        if self.priority != other.priority: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.cookie != other.cookie: return False
+        if self.packet_count != other.packet_count: return False
+        if self.byte_count != other.byte_count: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_stats_entry {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("table_id = ");
+                q.text("%#x" % self.table_id)
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("duration_sec = ");
+                q.text("%#x" % self.duration_sec)
+                q.text(","); q.breakable()
+                q.text("duration_nsec = ");
+                q.text("%#x" % self.duration_nsec)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("packet_count = ");
+                q.text("%#x" % self.packet_count)
+                q.text(","); q.breakable()
+                q.text("byte_count = ");
+                q.text("%#x" % self.byte_count)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+
+class match_v1(loxi.OFObject):
+
+    def __init__(self, wildcards=None, in_port=None, eth_src=None, eth_dst=None, vlan_vid=None, vlan_pcp=None, eth_type=None, ip_dscp=None, ip_proto=None, ipv4_src=None, ipv4_dst=None, tcp_src=None, tcp_dst=None):
+        if wildcards != None:
+            self.wildcards = wildcards
+        else:
+            self.wildcards = util.init_wc_bmap()
+        if in_port != None:
+            self.in_port = in_port
+        else:
+            self.in_port = 0
+        if eth_src != None:
+            self.eth_src = eth_src
+        else:
+            self.eth_src = [0,0,0,0,0,0]
+        if eth_dst != None:
+            self.eth_dst = eth_dst
+        else:
+            self.eth_dst = [0,0,0,0,0,0]
+        if vlan_vid != None:
+            self.vlan_vid = vlan_vid
+        else:
+            self.vlan_vid = 0
+        if vlan_pcp != None:
+            self.vlan_pcp = vlan_pcp
+        else:
+            self.vlan_pcp = 0
+        if eth_type != None:
+            self.eth_type = eth_type
+        else:
+            self.eth_type = 0
+        if ip_dscp != None:
+            self.ip_dscp = ip_dscp
+        else:
+            self.ip_dscp = 0
+        if ip_proto != None:
+            self.ip_proto = ip_proto
+        else:
+            self.ip_proto = 0
+        if ipv4_src != None:
+            self.ipv4_src = ipv4_src
+        else:
+            self.ipv4_src = 0
+        if ipv4_dst != None:
+            self.ipv4_dst = ipv4_dst
+        else:
+            self.ipv4_dst = 0
+        if tcp_src != None:
+            self.tcp_src = tcp_src
+        else:
+            self.tcp_src = 0
+        if tcp_dst != None:
+            self.tcp_dst = tcp_dst
+        else:
+            self.tcp_dst = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(util.pack_wc_bmap(self.wildcards))
+        packed.append(util.pack_port_no(self.in_port))
+        packed.append(struct.pack("!6B", *self.eth_src))
+        packed.append(struct.pack("!6B", *self.eth_dst))
+        packed.append(struct.pack("!H", self.vlan_vid))
+        packed.append(struct.pack("!B", self.vlan_pcp))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.eth_type))
+        packed.append(struct.pack("!B", self.ip_dscp))
+        packed.append(struct.pack("!B", self.ip_proto))
+        packed.append('\x00' * 2)
+        packed.append(struct.pack("!L", self.ipv4_src))
+        packed.append(struct.pack("!L", self.ipv4_dst))
+        packed.append(struct.pack("!H", self.tcp_src))
+        packed.append(struct.pack("!H", self.tcp_dst))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = match_v1()
+        obj.wildcards = util.unpack_wc_bmap(reader)
+        obj.in_port = util.unpack_port_no(reader)
+        obj.eth_src = list(reader.read('!6B'))
+        obj.eth_dst = list(reader.read('!6B'))
+        obj.vlan_vid = reader.read("!H")[0]
+        obj.vlan_pcp = reader.read("!B")[0]
+        reader.skip(1)
+        obj.eth_type = reader.read("!H")[0]
+        obj.ip_dscp = reader.read("!B")[0]
+        obj.ip_proto = reader.read("!B")[0]
+        reader.skip(2)
+        obj.ipv4_src = reader.read("!L")[0]
+        obj.ipv4_dst = reader.read("!L")[0]
+        obj.tcp_src = reader.read("!H")[0]
+        obj.tcp_dst = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.wildcards != other.wildcards: return False
+        if self.in_port != other.in_port: return False
+        if self.eth_src != other.eth_src: return False
+        if self.eth_dst != other.eth_dst: return False
+        if self.vlan_vid != other.vlan_vid: return False
+        if self.vlan_pcp != other.vlan_pcp: return False
+        if self.eth_type != other.eth_type: return False
+        if self.ip_dscp != other.ip_dscp: return False
+        if self.ip_proto != other.ip_proto: return False
+        if self.ipv4_src != other.ipv4_src: return False
+        if self.ipv4_dst != other.ipv4_dst: return False
+        if self.tcp_src != other.tcp_src: return False
+        if self.tcp_dst != other.tcp_dst: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("match_v1 {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("wildcards = ");
+                q.text(util.pretty_wildcards(self.wildcards))
+                q.text(","); q.breakable()
+                q.text("in_port = ");
+                q.text(util.pretty_port(self.in_port))
+                q.text(","); q.breakable()
+                q.text("eth_src = ");
+                q.text(util.pretty_mac(self.eth_src))
+                q.text(","); q.breakable()
+                q.text("eth_dst = ");
+                q.text(util.pretty_mac(self.eth_dst))
+                q.text(","); q.breakable()
+                q.text("vlan_vid = ");
+                q.text("%#x" % self.vlan_vid)
+                q.text(","); q.breakable()
+                q.text("vlan_pcp = ");
+                q.text("%#x" % self.vlan_pcp)
+                q.text(","); q.breakable()
+                q.text("eth_type = ");
+                q.text("%#x" % self.eth_type)
+                q.text(","); q.breakable()
+                q.text("ip_dscp = ");
+                q.text("%#x" % self.ip_dscp)
+                q.text(","); q.breakable()
+                q.text("ip_proto = ");
+                q.text("%#x" % self.ip_proto)
+                q.text(","); q.breakable()
+                q.text("ipv4_src = ");
+                q.text(util.pretty_ipv4(self.ipv4_src))
+                q.text(","); q.breakable()
+                q.text("ipv4_dst = ");
+                q.text(util.pretty_ipv4(self.ipv4_dst))
+                q.text(","); q.breakable()
+                q.text("tcp_src = ");
+                q.text("%#x" % self.tcp_src)
+                q.text(","); q.breakable()
+                q.text("tcp_dst = ");
+                q.text("%#x" % self.tcp_dst)
+            q.breakable()
+        q.text('}')
+
+
+class packet_queue(loxi.OFObject):
+
+    def __init__(self, queue_id=None, properties=None):
+        if queue_id != None:
+            self.queue_id = queue_id
+        else:
+            self.queue_id = 0
+        if properties != None:
+            self.properties = properties
+        else:
+            self.properties = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!L", self.queue_id))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append('\x00' * 2)
+        packed.append(loxi.generic_util.pack_list(self.properties))
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = packet_queue()
+        obj.queue_id = reader.read("!L")[0]
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 6)
+        reader.skip(2)
+        obj.properties = loxi.generic_util.unpack_list(reader, ofp.common.queue_prop.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.queue_id != other.queue_id: return False
+        if self.properties != other.properties: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("packet_queue {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("queue_id = ");
+                q.text("%#x" % self.queue_id)
+                q.text(","); q.breakable()
+                q.text("properties = ");
+                q.pp(self.properties)
+            q.breakable()
+        q.text('}')
+
+
+class port_desc(loxi.OFObject):
+
+    def __init__(self, port_no=None, hw_addr=None, name=None, config=None, state=None, curr=None, advertised=None, supported=None, peer=None):
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if hw_addr != None:
+            self.hw_addr = hw_addr
+        else:
+            self.hw_addr = [0,0,0,0,0,0]
+        if name != None:
+            self.name = name
+        else:
+            self.name = ""
+        if config != None:
+            self.config = config
+        else:
+            self.config = 0
+        if state != None:
+            self.state = state
+        else:
+            self.state = 0
+        if curr != None:
+            self.curr = curr
+        else:
+            self.curr = 0
+        if advertised != None:
+            self.advertised = advertised
+        else:
+            self.advertised = 0
+        if supported != None:
+            self.supported = supported
+        else:
+            self.supported = 0
+        if peer != None:
+            self.peer = peer
+        else:
+            self.peer = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!6B", *self.hw_addr))
+        packed.append(struct.pack("!16s", self.name))
+        packed.append(struct.pack("!L", self.config))
+        packed.append(struct.pack("!L", self.state))
+        packed.append(struct.pack("!L", self.curr))
+        packed.append(struct.pack("!L", self.advertised))
+        packed.append(struct.pack("!L", self.supported))
+        packed.append(struct.pack("!L", self.peer))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_desc()
+        obj.port_no = util.unpack_port_no(reader)
+        obj.hw_addr = list(reader.read('!6B'))
+        obj.name = reader.read("!16s")[0].rstrip("\x00")
+        obj.config = reader.read("!L")[0]
+        obj.state = reader.read("!L")[0]
+        obj.curr = reader.read("!L")[0]
+        obj.advertised = reader.read("!L")[0]
+        obj.supported = reader.read("!L")[0]
+        obj.peer = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port_no != other.port_no: return False
+        if self.hw_addr != other.hw_addr: return False
+        if self.name != other.name: return False
+        if self.config != other.config: return False
+        if self.state != other.state: return False
+        if self.curr != other.curr: return False
+        if self.advertised != other.advertised: return False
+        if self.supported != other.supported: return False
+        if self.peer != other.peer: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_desc {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("hw_addr = ");
+                q.text(util.pretty_mac(self.hw_addr))
+                q.text(","); q.breakable()
+                q.text("name = ");
+                q.pp(self.name)
+                q.text(","); q.breakable()
+                q.text("config = ");
+                q.text("%#x" % self.config)
+                q.text(","); q.breakable()
+                q.text("state = ");
+                q.text("%#x" % self.state)
+                q.text(","); q.breakable()
+                q.text("curr = ");
+                q.text("%#x" % self.curr)
+                q.text(","); q.breakable()
+                q.text("advertised = ");
+                q.text("%#x" % self.advertised)
+                q.text(","); q.breakable()
+                q.text("supported = ");
+                q.text("%#x" % self.supported)
+                q.text(","); q.breakable()
+                q.text("peer = ");
+                q.text("%#x" % self.peer)
+            q.breakable()
+        q.text('}')
+
+
+class port_stats_entry(loxi.OFObject):
+
+    def __init__(self, port_no=None, rx_packets=None, tx_packets=None, rx_bytes=None, tx_bytes=None, rx_dropped=None, tx_dropped=None, rx_errors=None, tx_errors=None, rx_frame_err=None, rx_over_err=None, rx_crc_err=None, collisions=None):
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if rx_packets != None:
+            self.rx_packets = rx_packets
+        else:
+            self.rx_packets = 0
+        if tx_packets != None:
+            self.tx_packets = tx_packets
+        else:
+            self.tx_packets = 0
+        if rx_bytes != None:
+            self.rx_bytes = rx_bytes
+        else:
+            self.rx_bytes = 0
+        if tx_bytes != None:
+            self.tx_bytes = tx_bytes
+        else:
+            self.tx_bytes = 0
+        if rx_dropped != None:
+            self.rx_dropped = rx_dropped
+        else:
+            self.rx_dropped = 0
+        if tx_dropped != None:
+            self.tx_dropped = tx_dropped
+        else:
+            self.tx_dropped = 0
+        if rx_errors != None:
+            self.rx_errors = rx_errors
+        else:
+            self.rx_errors = 0
+        if tx_errors != None:
+            self.tx_errors = tx_errors
+        else:
+            self.tx_errors = 0
+        if rx_frame_err != None:
+            self.rx_frame_err = rx_frame_err
+        else:
+            self.rx_frame_err = 0
+        if rx_over_err != None:
+            self.rx_over_err = rx_over_err
+        else:
+            self.rx_over_err = 0
+        if rx_crc_err != None:
+            self.rx_crc_err = rx_crc_err
+        else:
+            self.rx_crc_err = 0
+        if collisions != None:
+            self.collisions = collisions
+        else:
+            self.collisions = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append('\x00' * 6)
+        packed.append(struct.pack("!Q", self.rx_packets))
+        packed.append(struct.pack("!Q", self.tx_packets))
+        packed.append(struct.pack("!Q", self.rx_bytes))
+        packed.append(struct.pack("!Q", self.tx_bytes))
+        packed.append(struct.pack("!Q", self.rx_dropped))
+        packed.append(struct.pack("!Q", self.tx_dropped))
+        packed.append(struct.pack("!Q", self.rx_errors))
+        packed.append(struct.pack("!Q", self.tx_errors))
+        packed.append(struct.pack("!Q", self.rx_frame_err))
+        packed.append(struct.pack("!Q", self.rx_over_err))
+        packed.append(struct.pack("!Q", self.rx_crc_err))
+        packed.append(struct.pack("!Q", self.collisions))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_stats_entry()
+        obj.port_no = util.unpack_port_no(reader)
+        reader.skip(6)
+        obj.rx_packets = reader.read("!Q")[0]
+        obj.tx_packets = reader.read("!Q")[0]
+        obj.rx_bytes = reader.read("!Q")[0]
+        obj.tx_bytes = reader.read("!Q")[0]
+        obj.rx_dropped = reader.read("!Q")[0]
+        obj.tx_dropped = reader.read("!Q")[0]
+        obj.rx_errors = reader.read("!Q")[0]
+        obj.tx_errors = reader.read("!Q")[0]
+        obj.rx_frame_err = reader.read("!Q")[0]
+        obj.rx_over_err = reader.read("!Q")[0]
+        obj.rx_crc_err = reader.read("!Q")[0]
+        obj.collisions = reader.read("!Q")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port_no != other.port_no: return False
+        if self.rx_packets != other.rx_packets: return False
+        if self.tx_packets != other.tx_packets: return False
+        if self.rx_bytes != other.rx_bytes: return False
+        if self.tx_bytes != other.tx_bytes: return False
+        if self.rx_dropped != other.rx_dropped: return False
+        if self.tx_dropped != other.tx_dropped: return False
+        if self.rx_errors != other.rx_errors: return False
+        if self.tx_errors != other.tx_errors: return False
+        if self.rx_frame_err != other.rx_frame_err: return False
+        if self.rx_over_err != other.rx_over_err: return False
+        if self.rx_crc_err != other.rx_crc_err: return False
+        if self.collisions != other.collisions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_stats_entry {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("rx_packets = ");
+                q.text("%#x" % self.rx_packets)
+                q.text(","); q.breakable()
+                q.text("tx_packets = ");
+                q.text("%#x" % self.tx_packets)
+                q.text(","); q.breakable()
+                q.text("rx_bytes = ");
+                q.text("%#x" % self.rx_bytes)
+                q.text(","); q.breakable()
+                q.text("tx_bytes = ");
+                q.text("%#x" % self.tx_bytes)
+                q.text(","); q.breakable()
+                q.text("rx_dropped = ");
+                q.text("%#x" % self.rx_dropped)
+                q.text(","); q.breakable()
+                q.text("tx_dropped = ");
+                q.text("%#x" % self.tx_dropped)
+                q.text(","); q.breakable()
+                q.text("rx_errors = ");
+                q.text("%#x" % self.rx_errors)
+                q.text(","); q.breakable()
+                q.text("tx_errors = ");
+                q.text("%#x" % self.tx_errors)
+                q.text(","); q.breakable()
+                q.text("rx_frame_err = ");
+                q.text("%#x" % self.rx_frame_err)
+                q.text(","); q.breakable()
+                q.text("rx_over_err = ");
+                q.text("%#x" % self.rx_over_err)
+                q.text(","); q.breakable()
+                q.text("rx_crc_err = ");
+                q.text("%#x" % self.rx_crc_err)
+                q.text(","); q.breakable()
+                q.text("collisions = ");
+                q.text("%#x" % self.collisions)
+            q.breakable()
+        q.text('}')
+
+
+class queue_prop(loxi.OFObject):
+    subtypes = {}
+
+
+    def __init__(self, type=None):
+        if type != None:
+            self.type = type
+        else:
+            self.type = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 0)
+        subclass = queue_prop.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = queue_prop()
+        obj.type = reader.read("!H")[0]
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.type != other.type: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("queue_prop {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+            q.breakable()
+        q.text('}')
+
+
+class queue_prop_min_rate(queue_prop):
+    type = 1
+
+    def __init__(self, rate=None):
+        if rate != None:
+            self.rate = rate
+        else:
+            self.rate = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!H", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for len at index 1
+        packed.append('\x00' * 4)
+        packed.append(struct.pack("!H", self.rate))
+        packed.append('\x00' * 6)
+        length = sum([len(x) for x in packed])
+        packed[1] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = queue_prop_min_rate()
+        _type = reader.read("!H")[0]
+        assert(_type == 1)
+        _len = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_len, 4)
+        reader.skip(4)
+        obj.rate = reader.read("!H")[0]
+        reader.skip(6)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.rate != other.rate: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("queue_prop_min_rate {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("rate = ");
+                q.text("%#x" % self.rate)
+            q.breakable()
+        q.text('}')
+
+queue_prop.subtypes[1] = queue_prop_min_rate
+
+class queue_stats_entry(loxi.OFObject):
+
+    def __init__(self, port_no=None, queue_id=None, tx_bytes=None, tx_packets=None, tx_errors=None):
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if queue_id != None:
+            self.queue_id = queue_id
+        else:
+            self.queue_id = 0
+        if tx_bytes != None:
+            self.tx_bytes = tx_bytes
+        else:
+            self.tx_bytes = 0
+        if tx_packets != None:
+            self.tx_packets = tx_packets
+        else:
+            self.tx_packets = 0
+        if tx_errors != None:
+            self.tx_errors = tx_errors
+        else:
+            self.tx_errors = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append('\x00' * 2)
+        packed.append(struct.pack("!L", self.queue_id))
+        packed.append(struct.pack("!Q", self.tx_bytes))
+        packed.append(struct.pack("!Q", self.tx_packets))
+        packed.append(struct.pack("!Q", self.tx_errors))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = queue_stats_entry()
+        obj.port_no = util.unpack_port_no(reader)
+        reader.skip(2)
+        obj.queue_id = reader.read("!L")[0]
+        obj.tx_bytes = reader.read("!Q")[0]
+        obj.tx_packets = reader.read("!Q")[0]
+        obj.tx_errors = reader.read("!Q")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.port_no != other.port_no: return False
+        if self.queue_id != other.queue_id: return False
+        if self.tx_bytes != other.tx_bytes: return False
+        if self.tx_packets != other.tx_packets: return False
+        if self.tx_errors != other.tx_errors: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("queue_stats_entry {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("queue_id = ");
+                q.text("%#x" % self.queue_id)
+                q.text(","); q.breakable()
+                q.text("tx_bytes = ");
+                q.text("%#x" % self.tx_bytes)
+                q.text(","); q.breakable()
+                q.text("tx_packets = ");
+                q.text("%#x" % self.tx_packets)
+                q.text(","); q.breakable()
+                q.text("tx_errors = ");
+                q.text("%#x" % self.tx_errors)
+            q.breakable()
+        q.text('}')
+
+
+class table_stats_entry(loxi.OFObject):
+
+    def __init__(self, table_id=None, name=None, wildcards=None, max_entries=None, active_count=None, lookup_count=None, matched_count=None):
+        if table_id != None:
+            self.table_id = table_id
+        else:
+            self.table_id = 0
+        if name != None:
+            self.name = name
+        else:
+            self.name = ""
+        if wildcards != None:
+            self.wildcards = wildcards
+        else:
+            self.wildcards = util.init_wc_bmap()
+        if max_entries != None:
+            self.max_entries = max_entries
+        else:
+            self.max_entries = 0
+        if active_count != None:
+            self.active_count = active_count
+        else:
+            self.active_count = 0
+        if lookup_count != None:
+            self.lookup_count = lookup_count
+        else:
+            self.lookup_count = 0
+        if matched_count != None:
+            self.matched_count = matched_count
+        else:
+            self.matched_count = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.table_id))
+        packed.append('\x00' * 3)
+        packed.append(struct.pack("!32s", self.name))
+        packed.append(util.pack_wc_bmap(self.wildcards))
+        packed.append(struct.pack("!L", self.max_entries))
+        packed.append(struct.pack("!L", self.active_count))
+        packed.append(struct.pack("!Q", self.lookup_count))
+        packed.append(struct.pack("!Q", self.matched_count))
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = table_stats_entry()
+        obj.table_id = reader.read("!B")[0]
+        reader.skip(3)
+        obj.name = reader.read("!32s")[0].rstrip("\x00")
+        obj.wildcards = util.unpack_wc_bmap(reader)
+        obj.max_entries = reader.read("!L")[0]
+        obj.active_count = reader.read("!L")[0]
+        obj.lookup_count = reader.read("!Q")[0]
+        obj.matched_count = reader.read("!Q")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.table_id != other.table_id: return False
+        if self.name != other.name: return False
+        if self.wildcards != other.wildcards: return False
+        if self.max_entries != other.max_entries: return False
+        if self.active_count != other.active_count: return False
+        if self.lookup_count != other.lookup_count: return False
+        if self.matched_count != other.matched_count: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("table_stats_entry {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("table_id = ");
+                q.text("%#x" % self.table_id)
+                q.text(","); q.breakable()
+                q.text("name = ");
+                q.pp(self.name)
+                q.text(","); q.breakable()
+                q.text("wildcards = ");
+                q.text(util.pretty_wildcards(self.wildcards))
+                q.text(","); q.breakable()
+                q.text("max_entries = ");
+                q.text("%#x" % self.max_entries)
+                q.text(","); q.breakable()
+                q.text("active_count = ");
+                q.text("%#x" % self.active_count)
+                q.text(","); q.breakable()
+                q.text("lookup_count = ");
+                q.text("%#x" % self.lookup_count)
+                q.text(","); q.breakable()
+                q.text("matched_count = ");
+                q.text("%#x" % self.matched_count)
+            q.breakable()
+        q.text('}')
+
+
+
+match = match_v1
diff --git a/python/ofagent/loxi/of10/const.py b/python/ofagent/loxi/of10/const.py
new file mode 100644
index 0000000..01404ee
--- /dev/null
+++ b/python/ofagent/loxi/of10/const.py
@@ -0,0 +1,535 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+
+# Automatically generated by LOXI from template const.py
+# Do not modify
+
+OFP_VERSION = 1
+
+# Identifiers from group macro_definitions
+OFP_MAX_TABLE_NAME_LEN = 32
+OFP_MAX_PORT_NAME_LEN = 16
+OFP_TCP_PORT = 6653
+OFP_SSL_PORT = 6653
+OFP_ETH_ALEN = 6
+OFP_DEFAULT_MISS_SEND_LEN = 128
+OFP_VLAN_NONE = 65535
+OFPFW_ICMP_TYPE = 64
+OFPFW_ICMP_CODE = 128
+OFP_DL_TYPE_ETH2_CUTOFF = 1536
+OFP_DL_TYPE_NOT_ETH_TYPE = 1535
+OFP_FLOW_PERMANENT = 0
+OFP_DEFAULT_PRIORITY = 32768
+DESC_STR_LEN = 256
+SERIAL_NUM_LEN = 32
+OFPQ_ALL = 4294967295
+OFPQ_MIN_RATE_UNCFG = 65535
+
+# Identifiers from group of_bsn_pdu_slot_num
+BSN_PDU_SLOT_NUM_ANY = 255
+
+of_bsn_pdu_slot_num_map = {
+    255: 'BSN_PDU_SLOT_NUM_ANY',
+}
+
+# Identifiers from group ofp_action_type
+OFPAT_OUTPUT = 0
+OFPAT_SET_VLAN_VID = 1
+OFPAT_SET_VLAN_PCP = 2
+OFPAT_STRIP_VLAN = 3
+OFPAT_SET_DL_SRC = 4
+OFPAT_SET_DL_DST = 5
+OFPAT_SET_NW_SRC = 6
+OFPAT_SET_NW_DST = 7
+OFPAT_SET_NW_TOS = 8
+OFPAT_SET_TP_SRC = 9
+OFPAT_SET_TP_DST = 10
+OFPAT_ENQUEUE = 11
+OFPAT_EXPERIMENTER = 65535
+
+ofp_action_type_map = {
+    0: 'OFPAT_OUTPUT',
+    1: 'OFPAT_SET_VLAN_VID',
+    2: 'OFPAT_SET_VLAN_PCP',
+    3: 'OFPAT_STRIP_VLAN',
+    4: 'OFPAT_SET_DL_SRC',
+    5: 'OFPAT_SET_DL_DST',
+    6: 'OFPAT_SET_NW_SRC',
+    7: 'OFPAT_SET_NW_DST',
+    8: 'OFPAT_SET_NW_TOS',
+    9: 'OFPAT_SET_TP_SRC',
+    10: 'OFPAT_SET_TP_DST',
+    11: 'OFPAT_ENQUEUE',
+    65535: 'OFPAT_EXPERIMENTER',
+}
+
+# Identifiers from group ofp_bad_action_code
+OFPBAC_BAD_TYPE = 0
+OFPBAC_BAD_LEN = 1
+OFPBAC_BAD_EXPERIMENTER = 2
+OFPBAC_BAD_EXPERIMENTER_TYPE = 3
+OFPBAC_BAD_OUT_PORT = 4
+OFPBAC_BAD_ARGUMENT = 5
+OFPBAC_EPERM = 6
+OFPBAC_TOO_MANY = 7
+OFPBAC_BAD_QUEUE = 8
+
+ofp_bad_action_code_map = {
+    0: 'OFPBAC_BAD_TYPE',
+    1: 'OFPBAC_BAD_LEN',
+    2: 'OFPBAC_BAD_EXPERIMENTER',
+    3: 'OFPBAC_BAD_EXPERIMENTER_TYPE',
+    4: 'OFPBAC_BAD_OUT_PORT',
+    5: 'OFPBAC_BAD_ARGUMENT',
+    6: 'OFPBAC_EPERM',
+    7: 'OFPBAC_TOO_MANY',
+    8: 'OFPBAC_BAD_QUEUE',
+}
+
+# Identifiers from group ofp_bad_request_code
+OFPBRC_BAD_VERSION = 0
+OFPBRC_BAD_TYPE = 1
+OFPBRC_BAD_STAT = 2
+OFPBRC_BAD_EXPERIMENTER = 3
+OFPBRC_BAD_SUBTYPE = 4
+OFPBRC_EPERM = 5
+OFPBRC_BAD_LEN = 6
+OFPBRC_BUFFER_EMPTY = 7
+OFPBRC_BUFFER_UNKNOWN = 8
+
+ofp_bad_request_code_map = {
+    0: 'OFPBRC_BAD_VERSION',
+    1: 'OFPBRC_BAD_TYPE',
+    2: 'OFPBRC_BAD_STAT',
+    3: 'OFPBRC_BAD_EXPERIMENTER',
+    4: 'OFPBRC_BAD_SUBTYPE',
+    5: 'OFPBRC_EPERM',
+    6: 'OFPBRC_BAD_LEN',
+    7: 'OFPBRC_BUFFER_EMPTY',
+    8: 'OFPBRC_BUFFER_UNKNOWN',
+}
+
+# Identifiers from group ofp_bsn_vport_l2gre_flags
+OF_BSN_VPORT_L2GRE_LOCAL_MAC_IS_VALID = 1
+OF_BSN_VPORT_L2GRE_DSCP_ASSIGN = 2
+OF_BSN_VPORT_L2GRE_DSCP_COPY = 4
+OF_BSN_VPORT_L2GRE_LOOPBACK_IS_VALID = 8
+OF_BSN_VPORT_L2GRE_RATE_LIMIT_IS_VALID = 16
+
+ofp_bsn_vport_l2gre_flags_map = {
+    1: 'OF_BSN_VPORT_L2GRE_LOCAL_MAC_IS_VALID',
+    2: 'OF_BSN_VPORT_L2GRE_DSCP_ASSIGN',
+    4: 'OF_BSN_VPORT_L2GRE_DSCP_COPY',
+    8: 'OF_BSN_VPORT_L2GRE_LOOPBACK_IS_VALID',
+    16: 'OF_BSN_VPORT_L2GRE_RATE_LIMIT_IS_VALID',
+}
+
+# Identifiers from group ofp_bsn_vport_q_in_q_untagged
+OF_BSN_VPORT_Q_IN_Q_UNTAGGED = 65535
+
+ofp_bsn_vport_q_in_q_untagged_map = {
+    65535: 'OF_BSN_VPORT_Q_IN_Q_UNTAGGED',
+}
+
+# Identifiers from group ofp_bsn_vport_status
+OF_BSN_VPORT_STATUS_OK = 0
+OF_BSN_VPORT_STATUS_FAILED = 1
+
+ofp_bsn_vport_status_map = {
+    0: 'OF_BSN_VPORT_STATUS_OK',
+    1: 'OF_BSN_VPORT_STATUS_FAILED',
+}
+
+# Identifiers from group ofp_capabilities
+OFPC_FLOW_STATS = 1
+OFPC_TABLE_STATS = 2
+OFPC_PORT_STATS = 4
+OFPC_STP = 8
+OFPC_RESERVED = 16
+OFPC_IP_REASM = 32
+OFPC_QUEUE_STATS = 64
+OFPC_ARP_MATCH_IP = 128
+
+ofp_capabilities_map = {
+    1: 'OFPC_FLOW_STATS',
+    2: 'OFPC_TABLE_STATS',
+    4: 'OFPC_PORT_STATS',
+    8: 'OFPC_STP',
+    16: 'OFPC_RESERVED',
+    32: 'OFPC_IP_REASM',
+    64: 'OFPC_QUEUE_STATS',
+    128: 'OFPC_ARP_MATCH_IP',
+}
+
+# Identifiers from group ofp_config_flags
+OFPC_FRAG_NORMAL = 0
+OFPC_FRAG_DROP = 1
+OFPC_FRAG_REASM = 2
+OFPC_FRAG_MASK = 3
+
+ofp_config_flags_map = {
+    0: 'OFPC_FRAG_NORMAL',
+    1: 'OFPC_FRAG_DROP',
+    2: 'OFPC_FRAG_REASM',
+    3: 'OFPC_FRAG_MASK',
+}
+
+# Identifiers from group ofp_error_type
+OFPET_HELLO_FAILED = 0
+OFPET_BAD_REQUEST = 1
+OFPET_BAD_ACTION = 2
+OFPET_FLOW_MOD_FAILED = 3
+OFPET_PORT_MOD_FAILED = 4
+OFPET_QUEUE_OP_FAILED = 5
+
+ofp_error_type_map = {
+    0: 'OFPET_HELLO_FAILED',
+    1: 'OFPET_BAD_REQUEST',
+    2: 'OFPET_BAD_ACTION',
+    3: 'OFPET_FLOW_MOD_FAILED',
+    4: 'OFPET_PORT_MOD_FAILED',
+    5: 'OFPET_QUEUE_OP_FAILED',
+}
+
+# Identifiers from group ofp_flow_mod_command
+OFPFC_ADD = 0
+OFPFC_MODIFY = 1
+OFPFC_MODIFY_STRICT = 2
+OFPFC_DELETE = 3
+OFPFC_DELETE_STRICT = 4
+
+ofp_flow_mod_command_map = {
+    0: 'OFPFC_ADD',
+    1: 'OFPFC_MODIFY',
+    2: 'OFPFC_MODIFY_STRICT',
+    3: 'OFPFC_DELETE',
+    4: 'OFPFC_DELETE_STRICT',
+}
+
+# Identifiers from group ofp_flow_mod_failed_code
+OFPFMFC_ALL_TABLES_FULL = 0
+OFPFMFC_OVERLAP = 1
+OFPFMFC_EPERM = 2
+OFPFMFC_BAD_EMERG_TIMEOUT = 3
+OFPFMFC_BAD_COMMAND = 4
+OFPFMFC_UNSUPPORTED = 5
+
+ofp_flow_mod_failed_code_map = {
+    0: 'OFPFMFC_ALL_TABLES_FULL',
+    1: 'OFPFMFC_OVERLAP',
+    2: 'OFPFMFC_EPERM',
+    3: 'OFPFMFC_BAD_EMERG_TIMEOUT',
+    4: 'OFPFMFC_BAD_COMMAND',
+    5: 'OFPFMFC_UNSUPPORTED',
+}
+
+# Identifiers from group ofp_flow_mod_flags
+OFPFF_SEND_FLOW_REM = 1
+OFPFF_CHECK_OVERLAP = 2
+OFPFF_EMERG = 4
+
+ofp_flow_mod_flags_map = {
+    1: 'OFPFF_SEND_FLOW_REM',
+    2: 'OFPFF_CHECK_OVERLAP',
+    4: 'OFPFF_EMERG',
+}
+
+# Identifiers from group ofp_flow_removed_reason
+OFPRR_IDLE_TIMEOUT = 0
+OFPRR_HARD_TIMEOUT = 1
+OFPRR_DELETE = 2
+
+ofp_flow_removed_reason_map = {
+    0: 'OFPRR_IDLE_TIMEOUT',
+    1: 'OFPRR_HARD_TIMEOUT',
+    2: 'OFPRR_DELETE',
+}
+
+# Identifiers from group ofp_flow_wildcards
+OFPFW_IN_PORT = 1
+OFPFW_DL_VLAN = 2
+OFPFW_DL_SRC = 4
+OFPFW_NW_DST_BITS = 6
+OFPFW_NW_SRC_BITS = 6
+OFPFW_NW_SRC_SHIFT = 8
+OFPFW_DL_DST = 8
+OFPFW_NW_DST_SHIFT = 14
+OFPFW_DL_TYPE = 16
+OFPFW_NW_PROTO = 32
+OFPFW_TP_SRC = 64
+OFPFW_TP_DST = 128
+OFPFW_NW_SRC_ALL = 8192
+OFPFW_NW_SRC_MASK = 16128
+OFPFW_NW_DST_ALL = 524288
+OFPFW_NW_DST_MASK = 1032192
+OFPFW_DL_VLAN_PCP = 1048576
+OFPFW_NW_TOS = 2097152
+OFPFW_ALL = 4194303
+
+ofp_flow_wildcards_map = {
+    1: 'OFPFW_IN_PORT',
+    2: 'OFPFW_DL_VLAN',
+    4: 'OFPFW_DL_SRC',
+    8: 'OFPFW_DL_DST',
+    16: 'OFPFW_DL_TYPE',
+    32: 'OFPFW_NW_PROTO',
+    64: 'OFPFW_TP_SRC',
+    128: 'OFPFW_TP_DST',
+    1048576: 'OFPFW_DL_VLAN_PCP',
+    2097152: 'OFPFW_NW_TOS',
+}
+
+# Identifiers from group ofp_hello_failed_code
+OFPHFC_INCOMPATIBLE = 0
+OFPHFC_EPERM = 1
+
+ofp_hello_failed_code_map = {
+    0: 'OFPHFC_INCOMPATIBLE',
+    1: 'OFPHFC_EPERM',
+}
+
+# Identifiers from group ofp_nicira_controller_role
+NX_ROLE_OTHER = 0
+NX_ROLE_MASTER = 1
+NX_ROLE_SLAVE = 2
+
+ofp_nicira_controller_role_map = {
+    0: 'NX_ROLE_OTHER',
+    1: 'NX_ROLE_MASTER',
+    2: 'NX_ROLE_SLAVE',
+}
+
+# Identifiers from group ofp_packet_in_reason
+OFPR_NO_MATCH = 0
+OFPR_ACTION = 1
+
+ofp_packet_in_reason_map = {
+    0: 'OFPR_NO_MATCH',
+    1: 'OFPR_ACTION',
+}
+
+# Identifiers from group ofp_port
+OFPP_MAX = 0xff00
+OFPP_IN_PORT = 0xfff8
+OFPP_TABLE = 0xfff9
+OFPP_NORMAL = 0xfffa
+OFPP_FLOOD = 0xfffb
+OFPP_ALL = 0xfffc
+OFPP_CONTROLLER = 0xfffd
+OFPP_LOCAL = 0xfffe
+OFPP_NONE = 0xffff
+
+ofp_port_map = {
+    0xff00: 'OFPP_MAX',
+    0xfff8: 'OFPP_IN_PORT',
+    0xfff9: 'OFPP_TABLE',
+    0xfffa: 'OFPP_NORMAL',
+    0xfffb: 'OFPP_FLOOD',
+    0xfffc: 'OFPP_ALL',
+    0xfffd: 'OFPP_CONTROLLER',
+    0xfffe: 'OFPP_LOCAL',
+    0xffff: 'OFPP_NONE',
+}
+
+# Identifiers from group ofp_port_config
+OFPPC_PORT_DOWN = 1
+OFPPC_NO_STP = 2
+OFPPC_NO_RECV = 4
+OFPPC_NO_RECV_STP = 8
+OFPPC_NO_FLOOD = 16
+OFPPC_NO_FWD = 32
+OFPPC_NO_PACKET_IN = 64
+OFPPC_BSN_MIRROR_DEST = 2147483648
+
+ofp_port_config_map = {
+    1: 'OFPPC_PORT_DOWN',
+    2: 'OFPPC_NO_STP',
+    4: 'OFPPC_NO_RECV',
+    8: 'OFPPC_NO_RECV_STP',
+    16: 'OFPPC_NO_FLOOD',
+    32: 'OFPPC_NO_FWD',
+    64: 'OFPPC_NO_PACKET_IN',
+    2147483648: 'OFPPC_BSN_MIRROR_DEST',
+}
+
+# Identifiers from group ofp_port_features
+OFPPF_10MB_HD = 1
+OFPPF_10MB_FD = 2
+OFPPF_100MB_HD = 4
+OFPPF_100MB_FD = 8
+OFPPF_1GB_HD = 16
+OFPPF_1GB_FD = 32
+OFPPF_10GB_FD = 64
+OFPPF_COPPER = 128
+OFPPF_FIBER = 256
+OFPPF_AUTONEG = 512
+OFPPF_PAUSE = 1024
+OFPPF_PAUSE_ASYM = 2048
+
+ofp_port_features_map = {
+    1: 'OFPPF_10MB_HD',
+    2: 'OFPPF_10MB_FD',
+    4: 'OFPPF_100MB_HD',
+    8: 'OFPPF_100MB_FD',
+    16: 'OFPPF_1GB_HD',
+    32: 'OFPPF_1GB_FD',
+    64: 'OFPPF_10GB_FD',
+    128: 'OFPPF_COPPER',
+    256: 'OFPPF_FIBER',
+    512: 'OFPPF_AUTONEG',
+    1024: 'OFPPF_PAUSE',
+    2048: 'OFPPF_PAUSE_ASYM',
+}
+
+# Identifiers from group ofp_port_mod_failed_code
+OFPPMFC_BAD_PORT = 0
+OFPPMFC_BAD_HW_ADDR = 1
+
+ofp_port_mod_failed_code_map = {
+    0: 'OFPPMFC_BAD_PORT',
+    1: 'OFPPMFC_BAD_HW_ADDR',
+}
+
+# Identifiers from group ofp_port_reason
+OFPPR_ADD = 0
+OFPPR_DELETE = 1
+OFPPR_MODIFY = 2
+
+ofp_port_reason_map = {
+    0: 'OFPPR_ADD',
+    1: 'OFPPR_DELETE',
+    2: 'OFPPR_MODIFY',
+}
+
+# Identifiers from group ofp_port_state
+OFPPS_LINK_DOWN = 1
+OFPPS_STP_LISTEN = 0
+OFPPS_STP_LEARN = 256
+OFPPS_STP_FORWARD = 512
+OFPPS_STP_BLOCK = 768
+OFPPS_STP_MASK = 768
+
+ofp_port_state_map = {
+    1: 'OFPPS_LINK_DOWN',
+    0: 'OFPPS_STP_LISTEN',
+    256: 'OFPPS_STP_LEARN',
+    512: 'OFPPS_STP_FORWARD',
+    768: 'OFPPS_STP_BLOCK',
+    768: 'OFPPS_STP_MASK',
+}
+
+# Identifiers from group ofp_queue_op_failed_code
+OFPQOFC_BAD_PORT = 0
+OFPQOFC_BAD_QUEUE = 1
+OFPQOFC_EPERM = 2
+
+ofp_queue_op_failed_code_map = {
+    0: 'OFPQOFC_BAD_PORT',
+    1: 'OFPQOFC_BAD_QUEUE',
+    2: 'OFPQOFC_EPERM',
+}
+
+# Identifiers from group ofp_queue_properties
+OFPQT_NONE = 0
+OFPQT_MIN_RATE = 1
+
+ofp_queue_properties_map = {
+    0: 'OFPQT_NONE',
+    1: 'OFPQT_MIN_RATE',
+}
+
+# Identifiers from group ofp_stats_reply_flags
+OFPSF_REPLY_MORE = 1
+
+ofp_stats_reply_flags_map = {
+    1: 'OFPSF_REPLY_MORE',
+}
+
+# Identifiers from group ofp_stats_request_flags
+
+ofp_stats_request_flags_map = {
+}
+
+# Identifiers from group ofp_stats_type
+OFPST_DESC = 0
+OFPST_FLOW = 1
+OFPST_AGGREGATE = 2
+OFPST_TABLE = 3
+OFPST_PORT = 4
+OFPST_QUEUE = 5
+OFPST_EXPERIMENTER = 65535
+
+ofp_stats_type_map = {
+    0: 'OFPST_DESC',
+    1: 'OFPST_FLOW',
+    2: 'OFPST_AGGREGATE',
+    3: 'OFPST_TABLE',
+    4: 'OFPST_PORT',
+    5: 'OFPST_QUEUE',
+    65535: 'OFPST_EXPERIMENTER',
+}
+
+# Identifiers from group ofp_type
+OFPT_HELLO = 0
+OFPT_ERROR = 1
+OFPT_ECHO_REQUEST = 2
+OFPT_ECHO_REPLY = 3
+OFPT_EXPERIMENTER = 4
+OFPT_FEATURES_REQUEST = 5
+OFPT_FEATURES_REPLY = 6
+OFPT_GET_CONFIG_REQUEST = 7
+OFPT_GET_CONFIG_REPLY = 8
+OFPT_SET_CONFIG = 9
+OFPT_PACKET_IN = 10
+OFPT_FLOW_REMOVED = 11
+OFPT_PORT_STATUS = 12
+OFPT_PACKET_OUT = 13
+OFPT_FLOW_MOD = 14
+OFPT_PORT_MOD = 15
+OFPT_STATS_REQUEST = 16
+OFPT_STATS_REPLY = 17
+OFPT_BARRIER_REQUEST = 18
+OFPT_BARRIER_REPLY = 19
+OFPT_QUEUE_GET_CONFIG_REQUEST = 20
+OFPT_QUEUE_GET_CONFIG_REPLY = 21
+
+ofp_type_map = {
+    0: 'OFPT_HELLO',
+    1: 'OFPT_ERROR',
+    2: 'OFPT_ECHO_REQUEST',
+    3: 'OFPT_ECHO_REPLY',
+    4: 'OFPT_EXPERIMENTER',
+    5: 'OFPT_FEATURES_REQUEST',
+    6: 'OFPT_FEATURES_REPLY',
+    7: 'OFPT_GET_CONFIG_REQUEST',
+    8: 'OFPT_GET_CONFIG_REPLY',
+    9: 'OFPT_SET_CONFIG',
+    10: 'OFPT_PACKET_IN',
+    11: 'OFPT_FLOW_REMOVED',
+    12: 'OFPT_PORT_STATUS',
+    13: 'OFPT_PACKET_OUT',
+    14: 'OFPT_FLOW_MOD',
+    15: 'OFPT_PORT_MOD',
+    16: 'OFPT_STATS_REQUEST',
+    17: 'OFPT_STATS_REPLY',
+    18: 'OFPT_BARRIER_REQUEST',
+    19: 'OFPT_BARRIER_REPLY',
+    20: 'OFPT_QUEUE_GET_CONFIG_REQUEST',
+    21: 'OFPT_QUEUE_GET_CONFIG_REPLY',
+}
+
diff --git a/python/ofagent/loxi/of10/message.py b/python/ofagent/loxi/of10/message.py
new file mode 100644
index 0000000..8c3d46c
--- /dev/null
+++ b/python/ofagent/loxi/of10/message.py
@@ -0,0 +1,7550 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2011, 2012 Open Networking Foundation
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# See the file LICENSE.pyloxi which should have been included in the source distribution
+
+# Automatically generated by LOXI from template module.py
+# Do not modify
+
+import struct
+import loxi
+import util
+import loxi.generic_util
+
+import sys
+ofp = sys.modules['loxi.of10']
+
+class message(loxi.OFObject):
+    subtypes = {}
+
+    version = 1
+
+    def __init__(self, type=None, xid=None):
+        if type != None:
+            self.type = type
+        else:
+            self.type = 0
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('B', 1)
+        subclass = message.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = message()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        obj.type = reader.read("!B")[0]
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.type != other.type: return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("message {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+
+class stats_reply(message):
+    subtypes = {}
+
+    version = 1
+    type = 17
+
+    def __init__(self, xid=None, stats_type=None, flags=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if stats_type != None:
+            self.stats_type = stats_type
+        else:
+            self.stats_type = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 8)
+        subclass = stats_reply.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.stats_type = reader.read("!H")[0]
+        obj.flags = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.stats_type != other.stats_type: return False
+        if self.flags != other.flags: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[17] = stats_reply
+
+class aggregate_stats_reply(stats_reply):
+    version = 1
+    type = 17
+    stats_type = 2
+
+    def __init__(self, xid=None, flags=None, packet_count=None, byte_count=None, flow_count=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if packet_count != None:
+            self.packet_count = packet_count
+        else:
+            self.packet_count = 0
+        if byte_count != None:
+            self.byte_count = byte_count
+        else:
+            self.byte_count = 0
+        if flow_count != None:
+            self.flow_count = flow_count
+        else:
+            self.flow_count = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(struct.pack("!Q", self.packet_count))
+        packed.append(struct.pack("!Q", self.byte_count))
+        packed.append(struct.pack("!L", self.flow_count))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = aggregate_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 2)
+        obj.flags = reader.read("!H")[0]
+        obj.packet_count = reader.read("!Q")[0]
+        obj.byte_count = reader.read("!Q")[0]
+        obj.flow_count = reader.read("!L")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.packet_count != other.packet_count: return False
+        if self.byte_count != other.byte_count: return False
+        if self.flow_count != other.flow_count: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("aggregate_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("packet_count = ");
+                q.text("%#x" % self.packet_count)
+                q.text(","); q.breakable()
+                q.text("byte_count = ");
+                q.text("%#x" % self.byte_count)
+                q.text(","); q.breakable()
+                q.text("flow_count = ");
+                q.text("%#x" % self.flow_count)
+            q.breakable()
+        q.text('}')
+
+stats_reply.subtypes[2] = aggregate_stats_reply
+
+class stats_request(message):
+    subtypes = {}
+
+    version = 1
+    type = 16
+
+    def __init__(self, xid=None, stats_type=None, flags=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if stats_type != None:
+            self.stats_type = stats_type
+        else:
+            self.stats_type = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 8)
+        subclass = stats_request.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.stats_type = reader.read("!H")[0]
+        obj.flags = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.stats_type != other.stats_type: return False
+        if self.flags != other.flags: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[16] = stats_request
+
+class aggregate_stats_request(stats_request):
+    version = 1
+    type = 16
+    stats_type = 2
+
+    def __init__(self, xid=None, flags=None, match=None, table_id=None, out_port=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if table_id != None:
+            self.table_id = table_id
+        else:
+            self.table_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!B", self.table_id))
+        packed.append('\x00' * 1)
+        packed.append(util.pack_port_no(self.out_port))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = aggregate_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 2)
+        obj.flags = reader.read("!H")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.table_id = reader.read("!B")[0]
+        reader.skip(1)
+        obj.out_port = util.unpack_port_no(reader)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.match != other.match: return False
+        if self.table_id != other.table_id: return False
+        if self.out_port != other.out_port: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("aggregate_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("table_id = ");
+                q.text("%#x" % self.table_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+            q.breakable()
+        q.text('}')
+
+stats_request.subtypes[2] = aggregate_stats_request
+
+class error_msg(message):
+    subtypes = {}
+
+    version = 1
+    type = 1
+
+    def __init__(self, xid=None, err_type=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if err_type != None:
+            self.err_type = err_type
+        else:
+            self.err_type = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 8)
+        subclass = error_msg.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.err_type = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.err_type != other.err_type: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[1] = error_msg
+
+class bad_action_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 2
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bad_action_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 2)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bad_action_error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("code = ");
+                q.text("%#x" % self.code)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+error_msg.subtypes[2] = bad_action_error_msg
+
+class bad_request_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 1
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bad_request_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 1)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bad_request_error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("code = ");
+                q.text("%#x" % self.code)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+error_msg.subtypes[1] = bad_request_error_msg
+
+class barrier_reply(message):
+    version = 1
+    type = 19
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = barrier_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 19)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("barrier_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[19] = barrier_reply
+
+class barrier_request(message):
+    version = 1
+    type = 18
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = barrier_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 18)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("barrier_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[18] = barrier_request
+
+class experimenter(message):
+    subtypes = {}
+
+    version = 1
+    type = 4
+
+    def __init__(self, xid=None, experimenter=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if experimenter != None:
+            self.experimenter = experimenter
+        else:
+            self.experimenter = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 8)
+        subclass = experimenter.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = experimenter()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.experimenter = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.experimenter != other.experimenter: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("experimenter {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[4] = experimenter
+
+class bsn_header(experimenter):
+    subtypes = {}
+
+    version = 1
+    type = 4
+    experimenter = 6035143
+
+    def __init__(self, xid=None, subtype=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 12)
+        subclass = bsn_header.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = bsn_header()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        obj.subtype = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_header {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+experimenter.subtypes[6035143] = bsn_header
+
+class bsn_bw_clear_data_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 22
+
+    def __init__(self, xid=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_clear_data_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 22)
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_clear_data_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[22] = bsn_bw_clear_data_reply
+
+class bsn_bw_clear_data_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 21
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_clear_data_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 21)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_clear_data_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[21] = bsn_bw_clear_data_request
+
+class bsn_bw_enable_get_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 20
+
+    def __init__(self, xid=None, enabled=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if enabled != None:
+            self.enabled = enabled
+        else:
+            self.enabled = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.enabled))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_enable_get_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 20)
+        obj.enabled = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.enabled != other.enabled: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_enable_get_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("enabled = ");
+                q.text("%#x" % self.enabled)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[20] = bsn_bw_enable_get_reply
+
+class bsn_bw_enable_get_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 19
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_enable_get_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 19)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_enable_get_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[19] = bsn_bw_enable_get_request
+
+class bsn_bw_enable_set_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 23
+
+    def __init__(self, xid=None, enable=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if enable != None:
+            self.enable = enable
+        else:
+            self.enable = 0
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.enable))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_enable_set_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 23)
+        obj.enable = reader.read("!L")[0]
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.enable != other.enable: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_enable_set_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("enable = ");
+                q.text("%#x" % self.enable)
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[23] = bsn_bw_enable_set_reply
+
+class bsn_bw_enable_set_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 18
+
+    def __init__(self, xid=None, enable=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if enable != None:
+            self.enable = enable
+        else:
+            self.enable = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.enable))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_bw_enable_set_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 18)
+        obj.enable = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.enable != other.enable: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_bw_enable_set_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("enable = ");
+                q.text("%#x" % self.enable)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[18] = bsn_bw_enable_set_request
+
+class bsn_get_interfaces_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 10
+
+    def __init__(self, xid=None, interfaces=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if interfaces != None:
+            self.interfaces = interfaces
+        else:
+            self.interfaces = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(loxi.generic_util.pack_list(self.interfaces))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_interfaces_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 10)
+        obj.interfaces = loxi.generic_util.unpack_list(reader, ofp.common.bsn_interface.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.interfaces != other.interfaces: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_interfaces_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("interfaces = ");
+                q.pp(self.interfaces)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[10] = bsn_get_interfaces_reply
+
+class bsn_get_interfaces_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 9
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_interfaces_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 9)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_interfaces_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[9] = bsn_get_interfaces_request
+
+class bsn_get_ip_mask_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 2
+
+    def __init__(self, xid=None, index=None, mask=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if index != None:
+            self.index = index
+        else:
+            self.index = 0
+        if mask != None:
+            self.mask = mask
+        else:
+            self.mask = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.index))
+        packed.append('\x00' * 3)
+        packed.append(struct.pack("!L", self.mask))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_ip_mask_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 2)
+        obj.index = reader.read("!B")[0]
+        reader.skip(3)
+        obj.mask = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.index != other.index: return False
+        if self.mask != other.mask: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_ip_mask_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("index = ");
+                q.text("%#x" % self.index)
+                q.text(","); q.breakable()
+                q.text("mask = ");
+                q.text("%#x" % self.mask)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[2] = bsn_get_ip_mask_reply
+
+class bsn_get_ip_mask_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 1
+
+    def __init__(self, xid=None, index=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if index != None:
+            self.index = index
+        else:
+            self.index = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.index))
+        packed.append('\x00' * 7)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_ip_mask_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 1)
+        obj.index = reader.read("!B")[0]
+        reader.skip(7)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.index != other.index: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_ip_mask_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("index = ");
+                q.text("%#x" % self.index)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[1] = bsn_get_ip_mask_request
+
+class bsn_get_l2_table_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 14
+
+    def __init__(self, xid=None, l2_table_enable=None, l2_table_priority=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if l2_table_enable != None:
+            self.l2_table_enable = l2_table_enable
+        else:
+            self.l2_table_enable = 0
+        if l2_table_priority != None:
+            self.l2_table_priority = l2_table_priority
+        else:
+            self.l2_table_priority = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.l2_table_enable))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.l2_table_priority))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_l2_table_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 14)
+        obj.l2_table_enable = reader.read("!B")[0]
+        reader.skip(1)
+        obj.l2_table_priority = reader.read("!H")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.l2_table_enable != other.l2_table_enable: return False
+        if self.l2_table_priority != other.l2_table_priority: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_l2_table_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("l2_table_enable = ");
+                q.text("%#x" % self.l2_table_enable)
+                q.text(","); q.breakable()
+                q.text("l2_table_priority = ");
+                q.text("%#x" % self.l2_table_priority)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[14] = bsn_get_l2_table_reply
+
+class bsn_get_l2_table_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 13
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_l2_table_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 13)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_l2_table_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[13] = bsn_get_l2_table_request
+
+class bsn_get_mirroring_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 5
+
+    def __init__(self, xid=None, report_mirror_ports=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if report_mirror_ports != None:
+            self.report_mirror_ports = report_mirror_ports
+        else:
+            self.report_mirror_ports = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.report_mirror_ports))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_mirroring_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 5)
+        obj.report_mirror_ports = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.report_mirror_ports != other.report_mirror_ports: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_mirroring_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("report_mirror_ports = ");
+                q.text("%#x" % self.report_mirror_ports)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[5] = bsn_get_mirroring_reply
+
+class bsn_get_mirroring_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 4
+
+    def __init__(self, xid=None, report_mirror_ports=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if report_mirror_ports != None:
+            self.report_mirror_ports = report_mirror_ports
+        else:
+            self.report_mirror_ports = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.report_mirror_ports))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_get_mirroring_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 4)
+        obj.report_mirror_ports = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.report_mirror_ports != other.report_mirror_ports: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_get_mirroring_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("report_mirror_ports = ");
+                q.text("%#x" % self.report_mirror_ports)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[4] = bsn_get_mirroring_request
+
+class bsn_hybrid_get_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 28
+
+    def __init__(self, xid=None, hybrid_enable=None, hybrid_version=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if hybrid_enable != None:
+            self.hybrid_enable = hybrid_enable
+        else:
+            self.hybrid_enable = 0
+        if hybrid_version != None:
+            self.hybrid_version = hybrid_version
+        else:
+            self.hybrid_version = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.hybrid_enable))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.hybrid_version))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_hybrid_get_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 28)
+        obj.hybrid_enable = reader.read("!B")[0]
+        reader.skip(1)
+        obj.hybrid_version = reader.read("!H")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.hybrid_enable != other.hybrid_enable: return False
+        if self.hybrid_version != other.hybrid_version: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_hybrid_get_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("hybrid_enable = ");
+                q.text("%#x" % self.hybrid_enable)
+                q.text(","); q.breakable()
+                q.text("hybrid_version = ");
+                q.text("%#x" % self.hybrid_version)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[28] = bsn_hybrid_get_reply
+
+class bsn_hybrid_get_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 27
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_hybrid_get_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 27)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_hybrid_get_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[27] = bsn_hybrid_get_request
+
+class bsn_pdu_rx_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 34
+
+    def __init__(self, xid=None, status=None, port_no=None, slot_num=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if slot_num != None:
+            self.slot_num = slot_num
+        else:
+            self.slot_num = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!B", self.slot_num))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_pdu_rx_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 34)
+        obj.status = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.slot_num = reader.read("!B")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        if self.port_no != other.port_no: return False
+        if self.slot_num != other.slot_num: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_pdu_rx_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("slot_num = ");
+                q.text("%#x" % self.slot_num)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[34] = bsn_pdu_rx_reply
+
+class bsn_pdu_rx_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 33
+
+    def __init__(self, xid=None, timeout_ms=None, port_no=None, slot_num=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if timeout_ms != None:
+            self.timeout_ms = timeout_ms
+        else:
+            self.timeout_ms = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if slot_num != None:
+            self.slot_num = slot_num
+        else:
+            self.slot_num = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.timeout_ms))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!B", self.slot_num))
+        packed.append('\x00' * 3)
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_pdu_rx_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 33)
+        obj.timeout_ms = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.slot_num = reader.read("!B")[0]
+        reader.skip(3)
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.timeout_ms != other.timeout_ms: return False
+        if self.port_no != other.port_no: return False
+        if self.slot_num != other.slot_num: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_pdu_rx_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("timeout_ms = ");
+                q.text("%#x" % self.timeout_ms)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("slot_num = ");
+                q.text("%#x" % self.slot_num)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[33] = bsn_pdu_rx_request
+
+class bsn_pdu_rx_timeout(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 35
+
+    def __init__(self, xid=None, port_no=None, slot_num=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if slot_num != None:
+            self.slot_num = slot_num
+        else:
+            self.slot_num = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!B", self.slot_num))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_pdu_rx_timeout()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 35)
+        obj.port_no = util.unpack_port_no(reader)
+        obj.slot_num = reader.read("!B")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.port_no != other.port_no: return False
+        if self.slot_num != other.slot_num: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_pdu_rx_timeout {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("slot_num = ");
+                q.text("%#x" % self.slot_num)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[35] = bsn_pdu_rx_timeout
+
+class bsn_pdu_tx_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 32
+
+    def __init__(self, xid=None, status=None, port_no=None, slot_num=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if slot_num != None:
+            self.slot_num = slot_num
+        else:
+            self.slot_num = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!B", self.slot_num))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_pdu_tx_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 32)
+        obj.status = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.slot_num = reader.read("!B")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        if self.port_no != other.port_no: return False
+        if self.slot_num != other.slot_num: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_pdu_tx_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("slot_num = ");
+                q.text("%#x" % self.slot_num)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[32] = bsn_pdu_tx_reply
+
+class bsn_pdu_tx_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 31
+
+    def __init__(self, xid=None, tx_interval_ms=None, port_no=None, slot_num=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if tx_interval_ms != None:
+            self.tx_interval_ms = tx_interval_ms
+        else:
+            self.tx_interval_ms = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if slot_num != None:
+            self.slot_num = slot_num
+        else:
+            self.slot_num = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.tx_interval_ms))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!B", self.slot_num))
+        packed.append('\x00' * 3)
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_pdu_tx_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 31)
+        obj.tx_interval_ms = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.slot_num = reader.read("!B")[0]
+        reader.skip(3)
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.tx_interval_ms != other.tx_interval_ms: return False
+        if self.port_no != other.port_no: return False
+        if self.slot_num != other.slot_num: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_pdu_tx_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("tx_interval_ms = ");
+                q.text("%#x" % self.tx_interval_ms)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("slot_num = ");
+                q.text("%#x" % self.slot_num)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[31] = bsn_pdu_tx_request
+
+class bsn_set_ip_mask(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 0
+
+    def __init__(self, xid=None, index=None, mask=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if index != None:
+            self.index = index
+        else:
+            self.index = 0
+        if mask != None:
+            self.mask = mask
+        else:
+            self.mask = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.index))
+        packed.append('\x00' * 3)
+        packed.append(struct.pack("!L", self.mask))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_ip_mask()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 0)
+        obj.index = reader.read("!B")[0]
+        reader.skip(3)
+        obj.mask = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.index != other.index: return False
+        if self.mask != other.mask: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_ip_mask {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("index = ");
+                q.text("%#x" % self.index)
+                q.text(","); q.breakable()
+                q.text("mask = ");
+                q.text("%#x" % self.mask)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[0] = bsn_set_ip_mask
+
+class bsn_set_l2_table_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 24
+
+    def __init__(self, xid=None, l2_table_enable=None, l2_table_priority=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if l2_table_enable != None:
+            self.l2_table_enable = l2_table_enable
+        else:
+            self.l2_table_enable = 0
+        if l2_table_priority != None:
+            self.l2_table_priority = l2_table_priority
+        else:
+            self.l2_table_priority = 0
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.l2_table_enable))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.l2_table_priority))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_l2_table_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 24)
+        obj.l2_table_enable = reader.read("!B")[0]
+        reader.skip(1)
+        obj.l2_table_priority = reader.read("!H")[0]
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.l2_table_enable != other.l2_table_enable: return False
+        if self.l2_table_priority != other.l2_table_priority: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_l2_table_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("l2_table_enable = ");
+                q.text("%#x" % self.l2_table_enable)
+                q.text(","); q.breakable()
+                q.text("l2_table_priority = ");
+                q.text("%#x" % self.l2_table_priority)
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[24] = bsn_set_l2_table_reply
+
+class bsn_set_l2_table_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 12
+
+    def __init__(self, xid=None, l2_table_enable=None, l2_table_priority=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if l2_table_enable != None:
+            self.l2_table_enable = l2_table_enable
+        else:
+            self.l2_table_enable = 0
+        if l2_table_priority != None:
+            self.l2_table_priority = l2_table_priority
+        else:
+            self.l2_table_priority = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.l2_table_enable))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.l2_table_priority))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_l2_table_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 12)
+        obj.l2_table_enable = reader.read("!B")[0]
+        reader.skip(1)
+        obj.l2_table_priority = reader.read("!H")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.l2_table_enable != other.l2_table_enable: return False
+        if self.l2_table_priority != other.l2_table_priority: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_l2_table_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("l2_table_enable = ");
+                q.text("%#x" % self.l2_table_enable)
+                q.text(","); q.breakable()
+                q.text("l2_table_priority = ");
+                q.text("%#x" % self.l2_table_priority)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[12] = bsn_set_l2_table_request
+
+class bsn_set_mirroring(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 3
+
+    def __init__(self, xid=None, report_mirror_ports=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if report_mirror_ports != None:
+            self.report_mirror_ports = report_mirror_ports
+        else:
+            self.report_mirror_ports = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.report_mirror_ports))
+        packed.append('\x00' * 3)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_mirroring()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 3)
+        obj.report_mirror_ports = reader.read("!B")[0]
+        reader.skip(3)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.report_mirror_ports != other.report_mirror_ports: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_mirroring {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("report_mirror_ports = ");
+                q.text("%#x" % self.report_mirror_ports)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[3] = bsn_set_mirroring
+
+class bsn_set_pktin_suppression_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 25
+
+    def __init__(self, xid=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_pktin_suppression_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 25)
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_pktin_suppression_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[25] = bsn_set_pktin_suppression_reply
+
+class bsn_set_pktin_suppression_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 11
+
+    def __init__(self, xid=None, enabled=None, idle_timeout=None, hard_timeout=None, priority=None, cookie=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if enabled != None:
+            self.enabled = enabled
+        else:
+            self.enabled = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!B", self.enabled))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!Q", self.cookie))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_set_pktin_suppression_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 11)
+        obj.enabled = reader.read("!B")[0]
+        reader.skip(1)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.cookie = reader.read("!Q")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.enabled != other.enabled: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.cookie != other.cookie: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_set_pktin_suppression_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("enabled = ");
+                q.text("%#x" % self.enabled)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[11] = bsn_set_pktin_suppression_request
+
+class bsn_shell_command(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 6
+
+    def __init__(self, xid=None, service=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if service != None:
+            self.service = service
+        else:
+            self.service = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.service))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_shell_command()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 6)
+        obj.service = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.service != other.service: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_shell_command {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("service = ");
+                q.text("%#x" % self.service)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[6] = bsn_shell_command
+
+class bsn_shell_output(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 7
+
+    def __init__(self, xid=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_shell_output()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 7)
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_shell_output {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[7] = bsn_shell_output
+
+class bsn_shell_status(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 8
+
+    def __init__(self, xid=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_shell_status()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 8)
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_shell_status {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[8] = bsn_shell_status
+
+class experimenter_stats_reply(stats_reply):
+    subtypes = {}
+
+    version = 1
+    type = 17
+    stats_type = 65535
+
+    def __init__(self, xid=None, flags=None, experimenter=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if experimenter != None:
+            self.experimenter = experimenter
+        else:
+            self.experimenter = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 12)
+        subclass = experimenter_stats_reply.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = experimenter_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 65535)
+        obj.flags = reader.read("!H")[0]
+        obj.experimenter = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.experimenter != other.experimenter: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("experimenter_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+stats_reply.subtypes[65535] = experimenter_stats_reply
+
+class bsn_stats_reply(experimenter_stats_reply):
+    subtypes = {}
+
+    version = 1
+    type = 19
+    stats_type = 65535
+    experimenter = 6035143
+
+    def __init__(self, xid=None, flags=None, subtype=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append('\x00' * 4)
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 20)
+        subclass = bsn_stats_reply.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = bsn_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 19)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 65535)
+        obj.flags = reader.read("!H")[0]
+        reader.skip(4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        obj.subtype = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+            q.breakable()
+        q.text('}')
+
+experimenter_stats_reply.subtypes[6035143] = bsn_stats_reply
+
+class experimenter_stats_request(stats_request):
+    subtypes = {}
+
+    version = 1
+    type = 16
+    stats_type = 65535
+
+    def __init__(self, xid=None, flags=None, experimenter=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if experimenter != None:
+            self.experimenter = experimenter
+        else:
+            self.experimenter = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 12)
+        subclass = experimenter_stats_request.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = experimenter_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 65535)
+        obj.flags = reader.read("!H")[0]
+        obj.experimenter = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.experimenter != other.experimenter: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("experimenter_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+stats_request.subtypes[65535] = experimenter_stats_request
+
+class bsn_stats_request(experimenter_stats_request):
+    subtypes = {}
+
+    version = 1
+    type = 18
+    stats_type = 65535
+    experimenter = 6035143
+
+    def __init__(self, xid=None, flags=None, subtype=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append('\x00' * 4)
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 20)
+        subclass = bsn_stats_request.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = bsn_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 18)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 65535)
+        obj.flags = reader.read("!H")[0]
+        reader.skip(4)
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        obj.subtype = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+            q.breakable()
+        q.text('}')
+
+experimenter_stats_request.subtypes[6035143] = bsn_stats_request
+
+class bsn_virtual_port_create_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 16
+
+    def __init__(self, xid=None, status=None, vport_no=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        if vport_no != None:
+            self.vport_no = vport_no
+        else:
+            self.vport_no = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        packed.append(struct.pack("!L", self.vport_no))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_virtual_port_create_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 16)
+        obj.status = reader.read("!L")[0]
+        obj.vport_no = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        if self.vport_no != other.vport_no: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_virtual_port_create_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+                q.text(","); q.breakable()
+                q.text("vport_no = ");
+                q.text("%#x" % self.vport_no)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[16] = bsn_virtual_port_create_reply
+
+class bsn_virtual_port_create_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 15
+
+    def __init__(self, xid=None, vport=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if vport != None:
+            self.vport = vport
+        else:
+            self.vport = ofp.bsn_vport()
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(self.vport.pack())
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_virtual_port_create_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 15)
+        obj.vport = ofp.bsn_vport.unpack(reader)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.vport != other.vport: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_virtual_port_create_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("vport = ");
+                q.pp(self.vport)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[15] = bsn_virtual_port_create_request
+
+class bsn_virtual_port_remove_reply(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 26
+
+    def __init__(self, xid=None, status=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if status != None:
+            self.status = status
+        else:
+            self.status = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.status))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_virtual_port_remove_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 26)
+        obj.status = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.status != other.status: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_virtual_port_remove_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("status = ");
+                q.text("%#x" % self.status)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[26] = bsn_virtual_port_remove_reply
+
+class bsn_virtual_port_remove_request(bsn_header):
+    version = 1
+    type = 4
+    experimenter = 6035143
+    subtype = 17
+
+    def __init__(self, xid=None, vport_no=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if vport_no != None:
+            self.vport_no = vport_no
+        else:
+            self.vport_no = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.vport_no))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = bsn_virtual_port_remove_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 6035143)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 17)
+        obj.vport_no = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.vport_no != other.vport_no: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("bsn_virtual_port_remove_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("vport_no = ");
+                q.text("%#x" % self.vport_no)
+            q.breakable()
+        q.text('}')
+
+bsn_header.subtypes[17] = bsn_virtual_port_remove_request
+
+class desc_stats_reply(stats_reply):
+    version = 1
+    type = 17
+    stats_type = 0
+
+    def __init__(self, xid=None, flags=None, mfr_desc=None, hw_desc=None, sw_desc=None, serial_num=None, dp_desc=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if mfr_desc != None:
+            self.mfr_desc = mfr_desc
+        else:
+            self.mfr_desc = ""
+        if hw_desc != None:
+            self.hw_desc = hw_desc
+        else:
+            self.hw_desc = ""
+        if sw_desc != None:
+            self.sw_desc = sw_desc
+        else:
+            self.sw_desc = ""
+        if serial_num != None:
+            self.serial_num = serial_num
+        else:
+            self.serial_num = ""
+        if dp_desc != None:
+            self.dp_desc = dp_desc
+        else:
+            self.dp_desc = ""
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(struct.pack("!256s", self.mfr_desc))
+        packed.append(struct.pack("!256s", self.hw_desc))
+        packed.append(struct.pack("!256s", self.sw_desc))
+        packed.append(struct.pack("!32s", self.serial_num))
+        packed.append(struct.pack("!256s", self.dp_desc))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = desc_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 0)
+        obj.flags = reader.read("!H")[0]
+        obj.mfr_desc = reader.read("!256s")[0].rstrip("\x00")
+        obj.hw_desc = reader.read("!256s")[0].rstrip("\x00")
+        obj.sw_desc = reader.read("!256s")[0].rstrip("\x00")
+        obj.serial_num = reader.read("!32s")[0].rstrip("\x00")
+        obj.dp_desc = reader.read("!256s")[0].rstrip("\x00")
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.mfr_desc != other.mfr_desc: return False
+        if self.hw_desc != other.hw_desc: return False
+        if self.sw_desc != other.sw_desc: return False
+        if self.serial_num != other.serial_num: return False
+        if self.dp_desc != other.dp_desc: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("desc_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("mfr_desc = ");
+                q.pp(self.mfr_desc)
+                q.text(","); q.breakable()
+                q.text("hw_desc = ");
+                q.pp(self.hw_desc)
+                q.text(","); q.breakable()
+                q.text("sw_desc = ");
+                q.pp(self.sw_desc)
+                q.text(","); q.breakable()
+                q.text("serial_num = ");
+                q.pp(self.serial_num)
+                q.text(","); q.breakable()
+                q.text("dp_desc = ");
+                q.pp(self.dp_desc)
+            q.breakable()
+        q.text('}')
+
+stats_reply.subtypes[0] = desc_stats_reply
+
+class desc_stats_request(stats_request):
+    version = 1
+    type = 16
+    stats_type = 0
+
+    def __init__(self, xid=None, flags=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = desc_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 0)
+        obj.flags = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("desc_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+            q.breakable()
+        q.text('}')
+
+stats_request.subtypes[0] = desc_stats_request
+
+class echo_reply(message):
+    version = 1
+    type = 3
+
+    def __init__(self, xid=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = echo_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 3)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("echo_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[3] = echo_reply
+
+class echo_request(message):
+    version = 1
+    type = 2
+
+    def __init__(self, xid=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = echo_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 2)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("echo_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[2] = echo_request
+
+class features_reply(message):
+    version = 1
+    type = 6
+
+    def __init__(self, xid=None, datapath_id=None, n_buffers=None, n_tables=None, capabilities=None, actions=None, ports=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if datapath_id != None:
+            self.datapath_id = datapath_id
+        else:
+            self.datapath_id = 0
+        if n_buffers != None:
+            self.n_buffers = n_buffers
+        else:
+            self.n_buffers = 0
+        if n_tables != None:
+            self.n_tables = n_tables
+        else:
+            self.n_tables = 0
+        if capabilities != None:
+            self.capabilities = capabilities
+        else:
+            self.capabilities = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = 0
+        if ports != None:
+            self.ports = ports
+        else:
+            self.ports = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!Q", self.datapath_id))
+        packed.append(struct.pack("!L", self.n_buffers))
+        packed.append(struct.pack("!B", self.n_tables))
+        packed.append('\x00' * 3)
+        packed.append(struct.pack("!L", self.capabilities))
+        packed.append(struct.pack("!L", self.actions))
+        packed.append(loxi.generic_util.pack_list(self.ports))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = features_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 6)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.datapath_id = reader.read("!Q")[0]
+        obj.n_buffers = reader.read("!L")[0]
+        obj.n_tables = reader.read("!B")[0]
+        reader.skip(3)
+        obj.capabilities = reader.read("!L")[0]
+        obj.actions = reader.read("!L")[0]
+        obj.ports = loxi.generic_util.unpack_list(reader, ofp.common.port_desc.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.datapath_id != other.datapath_id: return False
+        if self.n_buffers != other.n_buffers: return False
+        if self.n_tables != other.n_tables: return False
+        if self.capabilities != other.capabilities: return False
+        if self.actions != other.actions: return False
+        if self.ports != other.ports: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("features_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("datapath_id = ");
+                q.text("%#x" % self.datapath_id)
+                q.text(","); q.breakable()
+                q.text("n_buffers = ");
+                q.text("%#x" % self.n_buffers)
+                q.text(","); q.breakable()
+                q.text("n_tables = ");
+                q.text("%#x" % self.n_tables)
+                q.text(","); q.breakable()
+                q.text("capabilities = ");
+                q.text("%#x" % self.capabilities)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.text("%#x" % self.actions)
+                q.text(","); q.breakable()
+                q.text("ports = ");
+                q.pp(self.ports)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[6] = features_reply
+
+class features_request(message):
+    version = 1
+    type = 5
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = features_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 5)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("features_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[5] = features_request
+
+class flow_mod(message):
+    subtypes = {}
+
+    version = 1
+    type = 14
+
+    def __init__(self, xid=None, match=None, cookie=None, _command=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if _command != None:
+            self._command = _command
+        else:
+            self._command = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!H', 56)
+        subclass = flow_mod.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = flow_mod()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        obj._command = util.unpack_fm_cmd(reader)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self._command != other._command: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_mod {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[14] = flow_mod
+
+class flow_add(flow_mod):
+    version = 1
+    type = 14
+    _command = 0
+
+    def __init__(self, xid=None, match=None, cookie=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_add()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        __command = util.unpack_fm_cmd(reader)
+        assert(__command == 0)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_add {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+flow_mod.subtypes[0] = flow_add
+
+class flow_delete(flow_mod):
+    version = 1
+    type = 14
+    _command = 3
+
+    def __init__(self, xid=None, match=None, cookie=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_delete()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        __command = util.unpack_fm_cmd(reader)
+        assert(__command == 3)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_delete {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+flow_mod.subtypes[3] = flow_delete
+
+class flow_delete_strict(flow_mod):
+    version = 1
+    type = 14
+    _command = 4
+
+    def __init__(self, xid=None, match=None, cookie=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_delete_strict()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        __command = util.unpack_fm_cmd(reader)
+        assert(__command == 4)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_delete_strict {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+flow_mod.subtypes[4] = flow_delete_strict
+
+class flow_mod_failed_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 3
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_mod_failed_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 3)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_mod_failed_error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("code = ");
+                q.text("%#x" % self.code)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+error_msg.subtypes[3] = flow_mod_failed_error_msg
+
+class flow_modify(flow_mod):
+    version = 1
+    type = 14
+    _command = 1
+
+    def __init__(self, xid=None, match=None, cookie=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_modify()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        __command = util.unpack_fm_cmd(reader)
+        assert(__command == 1)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_modify {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+flow_mod.subtypes[1] = flow_modify
+
+class flow_modify_strict(flow_mod):
+    version = 1
+    type = 14
+    _command = 2
+
+    def __init__(self, xid=None, match=None, cookie=None, idle_timeout=None, hard_timeout=None, priority=None, buffer_id=None, out_port=None, flags=None, actions=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if hard_timeout != None:
+            self.hard_timeout = hard_timeout
+        else:
+            self.hard_timeout = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(util.pack_fm_cmd(self._command))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append(struct.pack("!H", self.hard_timeout))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.out_port))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_modify_strict()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 14)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        __command = util.unpack_fm_cmd(reader)
+        assert(__command == 2)
+        obj.idle_timeout = reader.read("!H")[0]
+        obj.hard_timeout = reader.read("!H")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.out_port = util.unpack_port_no(reader)
+        obj.flags = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader, ofp.action.action.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.hard_timeout != other.hard_timeout: return False
+        if self.priority != other.priority: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.out_port != other.out_port: return False
+        if self.flags != other.flags: return False
+        if self.actions != other.actions: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_modify_strict {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("hard_timeout = ");
+                q.text("%#x" % self.hard_timeout)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+            q.breakable()
+        q.text('}')
+
+flow_mod.subtypes[2] = flow_modify_strict
+
+class flow_removed(message):
+    version = 1
+    type = 11
+
+    def __init__(self, xid=None, match=None, cookie=None, priority=None, reason=None, duration_sec=None, duration_nsec=None, idle_timeout=None, packet_count=None, byte_count=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if cookie != None:
+            self.cookie = cookie
+        else:
+            self.cookie = 0
+        if priority != None:
+            self.priority = priority
+        else:
+            self.priority = 0
+        if reason != None:
+            self.reason = reason
+        else:
+            self.reason = 0
+        if duration_sec != None:
+            self.duration_sec = duration_sec
+        else:
+            self.duration_sec = 0
+        if duration_nsec != None:
+            self.duration_nsec = duration_nsec
+        else:
+            self.duration_nsec = 0
+        if idle_timeout != None:
+            self.idle_timeout = idle_timeout
+        else:
+            self.idle_timeout = 0
+        if packet_count != None:
+            self.packet_count = packet_count
+        else:
+            self.packet_count = 0
+        if byte_count != None:
+            self.byte_count = byte_count
+        else:
+            self.byte_count = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!Q", self.cookie))
+        packed.append(struct.pack("!H", self.priority))
+        packed.append(struct.pack("!B", self.reason))
+        packed.append('\x00' * 1)
+        packed.append(struct.pack("!L", self.duration_sec))
+        packed.append(struct.pack("!L", self.duration_nsec))
+        packed.append(struct.pack("!H", self.idle_timeout))
+        packed.append('\x00' * 2)
+        packed.append(struct.pack("!Q", self.packet_count))
+        packed.append(struct.pack("!Q", self.byte_count))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_removed()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 11)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.cookie = reader.read("!Q")[0]
+        obj.priority = reader.read("!H")[0]
+        obj.reason = reader.read("!B")[0]
+        reader.skip(1)
+        obj.duration_sec = reader.read("!L")[0]
+        obj.duration_nsec = reader.read("!L")[0]
+        obj.idle_timeout = reader.read("!H")[0]
+        reader.skip(2)
+        obj.packet_count = reader.read("!Q")[0]
+        obj.byte_count = reader.read("!Q")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.match != other.match: return False
+        if self.cookie != other.cookie: return False
+        if self.priority != other.priority: return False
+        if self.reason != other.reason: return False
+        if self.duration_sec != other.duration_sec: return False
+        if self.duration_nsec != other.duration_nsec: return False
+        if self.idle_timeout != other.idle_timeout: return False
+        if self.packet_count != other.packet_count: return False
+        if self.byte_count != other.byte_count: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_removed {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("cookie = ");
+                q.text("%#x" % self.cookie)
+                q.text(","); q.breakable()
+                q.text("priority = ");
+                q.text("%#x" % self.priority)
+                q.text(","); q.breakable()
+                q.text("reason = ");
+                q.text("%#x" % self.reason)
+                q.text(","); q.breakable()
+                q.text("duration_sec = ");
+                q.text("%#x" % self.duration_sec)
+                q.text(","); q.breakable()
+                q.text("duration_nsec = ");
+                q.text("%#x" % self.duration_nsec)
+                q.text(","); q.breakable()
+                q.text("idle_timeout = ");
+                q.text("%#x" % self.idle_timeout)
+                q.text(","); q.breakable()
+                q.text("packet_count = ");
+                q.text("%#x" % self.packet_count)
+                q.text(","); q.breakable()
+                q.text("byte_count = ");
+                q.text("%#x" % self.byte_count)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[11] = flow_removed
+
+class flow_stats_reply(stats_reply):
+    version = 1
+    type = 17
+    stats_type = 1
+
+    def __init__(self, xid=None, flags=None, entries=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if entries != None:
+            self.entries = entries
+        else:
+            self.entries = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.entries))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 1)
+        obj.flags = reader.read("!H")[0]
+        obj.entries = loxi.generic_util.unpack_list(reader, ofp.common.flow_stats_entry.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.entries != other.entries: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("entries = ");
+                q.pp(self.entries)
+            q.breakable()
+        q.text('}')
+
+stats_reply.subtypes[1] = flow_stats_reply
+
+class flow_stats_request(stats_request):
+    version = 1
+    type = 16
+    stats_type = 1
+
+    def __init__(self, xid=None, flags=None, match=None, table_id=None, out_port=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if match != None:
+            self.match = match
+        else:
+            self.match = ofp.match()
+        if table_id != None:
+            self.table_id = table_id
+        else:
+            self.table_id = 0
+        if out_port != None:
+            self.out_port = out_port
+        else:
+            self.out_port = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(self.match.pack())
+        packed.append(struct.pack("!B", self.table_id))
+        packed.append('\x00' * 1)
+        packed.append(util.pack_port_no(self.out_port))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = flow_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 1)
+        obj.flags = reader.read("!H")[0]
+        obj.match = ofp.match.unpack(reader)
+        obj.table_id = reader.read("!B")[0]
+        reader.skip(1)
+        obj.out_port = util.unpack_port_no(reader)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.match != other.match: return False
+        if self.table_id != other.table_id: return False
+        if self.out_port != other.out_port: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("flow_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("match = ");
+                q.pp(self.match)
+                q.text(","); q.breakable()
+                q.text("table_id = ");
+                q.text("%#x" % self.table_id)
+                q.text(","); q.breakable()
+                q.text("out_port = ");
+                q.text(util.pretty_port(self.out_port))
+            q.breakable()
+        q.text('}')
+
+stats_request.subtypes[1] = flow_stats_request
+
+class get_config_reply(message):
+    version = 1
+    type = 8
+
+    def __init__(self, xid=None, flags=None, miss_send_len=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if miss_send_len != None:
+            self.miss_send_len = miss_send_len
+        else:
+            self.miss_send_len = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(struct.pack("!H", self.miss_send_len))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = get_config_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 8)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.flags = reader.read("!H")[0]
+        obj.miss_send_len = reader.read("!H")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.miss_send_len != other.miss_send_len: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("get_config_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("miss_send_len = ");
+                q.text("%#x" % self.miss_send_len)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[8] = get_config_reply
+
+class get_config_request(message):
+    version = 1
+    type = 7
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = get_config_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 7)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("get_config_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[7] = get_config_request
+
+class hello(message):
+    version = 1
+    type = 0
+
+    def __init__(self, xid=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = hello()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 0)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("hello {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+message.subtypes[0] = hello
+
+class hello_failed_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 0
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = hello_failed_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 0)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("hello_failed_error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("code = ");
+                q.text("%#x" % self.code)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+error_msg.subtypes[0] = hello_failed_error_msg
+
+class nicira_header(experimenter):
+    subtypes = {}
+
+    version = 1
+    type = 4
+    experimenter = 8992
+
+    def __init__(self, xid=None, subtype=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if subtype != None:
+            self.subtype = subtype
+        else:
+            self.subtype = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        subtype, = reader.peek('!L', 12)
+        subclass = nicira_header.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+        obj = nicira_header()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 8992)
+        obj.subtype = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.subtype != other.subtype: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("nicira_header {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+            q.breakable()
+        q.text('}')
+
+experimenter.subtypes[8992] = nicira_header
+
+class nicira_controller_role_reply(nicira_header):
+    version = 1
+    type = 4
+    experimenter = 8992
+    subtype = 11
+
+    def __init__(self, xid=None, role=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if role != None:
+            self.role = role
+        else:
+            self.role = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.role))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = nicira_controller_role_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 8992)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 11)
+        obj.role = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.role != other.role: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("nicira_controller_role_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("role = ");
+                q.text("%#x" % self.role)
+            q.breakable()
+        q.text('}')
+
+nicira_header.subtypes[11] = nicira_controller_role_reply
+
+class nicira_controller_role_request(nicira_header):
+    version = 1
+    type = 4
+    experimenter = 8992
+    subtype = 10
+
+    def __init__(self, xid=None, role=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if role != None:
+            self.role = role
+        else:
+            self.role = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.experimenter))
+        packed.append(struct.pack("!L", self.subtype))
+        packed.append(struct.pack("!L", self.role))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = nicira_controller_role_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 4)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _experimenter = reader.read("!L")[0]
+        assert(_experimenter == 8992)
+        _subtype = reader.read("!L")[0]
+        assert(_subtype == 10)
+        obj.role = reader.read("!L")[0]
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.role != other.role: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("nicira_controller_role_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("role = ");
+                q.text("%#x" % self.role)
+            q.breakable()
+        q.text('}')
+
+nicira_header.subtypes[10] = nicira_controller_role_request
+
+class packet_in(message):
+    version = 1
+    type = 10
+
+    def __init__(self, xid=None, buffer_id=None, total_len=None, in_port=None, reason=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if total_len != None:
+            self.total_len = total_len
+        else:
+            self.total_len = 0
+        if in_port != None:
+            self.in_port = in_port
+        else:
+            self.in_port = 0
+        if reason != None:
+            self.reason = reason
+        else:
+            self.reason = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(struct.pack("!H", self.total_len))
+        packed.append(util.pack_port_no(self.in_port))
+        packed.append(struct.pack("!B", self.reason))
+        packed.append('\x00' * 1)
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = packet_in()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 10)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.total_len = reader.read("!H")[0]
+        obj.in_port = util.unpack_port_no(reader)
+        obj.reason = reader.read("!B")[0]
+        reader.skip(1)
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.total_len != other.total_len: return False
+        if self.in_port != other.in_port: return False
+        if self.reason != other.reason: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("packet_in {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("total_len = ");
+                q.text("%#x" % self.total_len)
+                q.text(","); q.breakable()
+                q.text("in_port = ");
+                q.text(util.pretty_port(self.in_port))
+                q.text(","); q.breakable()
+                q.text("reason = ");
+                q.text("%#x" % self.reason)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[10] = packet_in
+
+class packet_out(message):
+    version = 1
+    type = 13
+
+    def __init__(self, xid=None, buffer_id=None, in_port=None, actions=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if buffer_id != None:
+            self.buffer_id = buffer_id
+        else:
+            self.buffer_id = 0
+        if in_port != None:
+            self.in_port = in_port
+        else:
+            self.in_port = 0
+        if actions != None:
+            self.actions = actions
+        else:
+            self.actions = []
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!L", self.buffer_id))
+        packed.append(util.pack_port_no(self.in_port))
+        packed.append(struct.pack("!H", 0)) # placeholder for actions_len at index 6
+        packed.append(loxi.generic_util.pack_list(self.actions))
+        packed[6] = struct.pack("!H", len(packed[-1]))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = packet_out()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 13)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.buffer_id = reader.read("!L")[0]
+        obj.in_port = util.unpack_port_no(reader)
+        _actions_len = reader.read("!H")[0]
+        obj.actions = loxi.generic_util.unpack_list(reader.slice(_actions_len), ofp.action.action.unpack)
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.buffer_id != other.buffer_id: return False
+        if self.in_port != other.in_port: return False
+        if self.actions != other.actions: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("packet_out {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("buffer_id = ");
+                q.text("%#x" % self.buffer_id)
+                q.text(","); q.breakable()
+                q.text("in_port = ");
+                q.text(util.pretty_port(self.in_port))
+                q.text(","); q.breakable()
+                q.text("actions = ");
+                q.pp(self.actions)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[13] = packet_out
+
+class port_mod(message):
+    version = 1
+    type = 15
+
+    def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, advertise=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        if hw_addr != None:
+            self.hw_addr = hw_addr
+        else:
+            self.hw_addr = [0,0,0,0,0,0]
+        if config != None:
+            self.config = config
+        else:
+            self.config = 0
+        if mask != None:
+            self.mask = mask
+        else:
+            self.mask = 0
+        if advertise != None:
+            self.advertise = advertise
+        else:
+            self.advertise = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append(struct.pack("!6B", *self.hw_addr))
+        packed.append(struct.pack("!L", self.config))
+        packed.append(struct.pack("!L", self.mask))
+        packed.append(struct.pack("!L", self.advertise))
+        packed.append('\x00' * 4)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_mod()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 15)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        obj.hw_addr = list(reader.read('!6B'))
+        obj.config = reader.read("!L")[0]
+        obj.mask = reader.read("!L")[0]
+        obj.advertise = reader.read("!L")[0]
+        reader.skip(4)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.port_no != other.port_no: return False
+        if self.hw_addr != other.hw_addr: return False
+        if self.config != other.config: return False
+        if self.mask != other.mask: return False
+        if self.advertise != other.advertise: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_mod {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+                q.text(","); q.breakable()
+                q.text("hw_addr = ");
+                q.text(util.pretty_mac(self.hw_addr))
+                q.text(","); q.breakable()
+                q.text("config = ");
+                q.text("%#x" % self.config)
+                q.text(","); q.breakable()
+                q.text("mask = ");
+                q.text("%#x" % self.mask)
+                q.text(","); q.breakable()
+                q.text("advertise = ");
+                q.text("%#x" % self.advertise)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[15] = port_mod
+
+class port_mod_failed_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 4
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_mod_failed_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 4)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_mod_failed_error_msg {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("code = ");
+                q.text("%#x" % self.code)
+                q.text(","); q.breakable()
+                q.text("data = ");
+                q.pp(self.data)
+            q.breakable()
+        q.text('}')
+
+error_msg.subtypes[4] = port_mod_failed_error_msg
+
+class port_stats_reply(stats_reply):
+    version = 1
+    type = 17
+    stats_type = 4
+
+    def __init__(self, xid=None, flags=None, entries=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if entries != None:
+            self.entries = entries
+        else:
+            self.entries = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(loxi.generic_util.pack_list(self.entries))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_stats_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 17)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 4)
+        obj.flags = reader.read("!H")[0]
+        obj.entries = loxi.generic_util.unpack_list(reader, ofp.common.port_stats_entry.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.entries != other.entries: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_stats_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("entries = ");
+                q.pp(self.entries)
+            q.breakable()
+        q.text('}')
+
+stats_reply.subtypes[4] = port_stats_reply
+
+class port_stats_request(stats_request):
+    version = 1
+    type = 16
+    stats_type = 4
+
+    def __init__(self, xid=None, flags=None, port_no=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if flags != None:
+            self.flags = flags
+        else:
+            self.flags = 0
+        if port_no != None:
+            self.port_no = port_no
+        else:
+            self.port_no = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.stats_type))
+        packed.append(struct.pack("!H", self.flags))
+        packed.append(util.pack_port_no(self.port_no))
+        packed.append('\x00' * 6)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_stats_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 16)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _stats_type = reader.read("!H")[0]
+        assert(_stats_type == 4)
+        obj.flags = reader.read("!H")[0]
+        obj.port_no = util.unpack_port_no(reader)
+        reader.skip(6)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.flags != other.flags: return False
+        if self.port_no != other.port_no: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_stats_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("flags = ");
+                q.text("%#x" % self.flags)
+                q.text(","); q.breakable()
+                q.text("port_no = ");
+                q.text(util.pretty_port(self.port_no))
+            q.breakable()
+        q.text('}')
+
+stats_request.subtypes[4] = port_stats_request
+
+class port_status(message):
+    version = 1
+    type = 12
+
+    def __init__(self, xid=None, reason=None, desc=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if reason != None:
+            self.reason = reason
+        else:
+            self.reason = 0
+        if desc != None:
+            self.desc = desc
+        else:
+            self.desc = ofp.port_desc()
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!B", self.reason))
+        packed.append('\x00' * 7)
+        packed.append(self.desc.pack())
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = port_status()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 12)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.reason = reader.read("!B")[0]
+        reader.skip(7)
+        obj.desc = ofp.port_desc.unpack(reader)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.reason != other.reason: return False
+        if self.desc != other.desc: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("port_status {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("reason = ");
+                q.text("%#x" % self.reason)
+                q.text(","); q.breakable()
+                q.text("desc = ");
+                q.pp(self.desc)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[12] = port_status
+
+class queue_get_config_reply(message):
+    version = 1
+    type = 21
+
+    def __init__(self, xid=None, port=None, queues=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if port != None:
+            self.port = port
+        else:
+            self.port = 0
+        if queues != None:
+            self.queues = queues
+        else:
+            self.queues = []
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(util.pack_port_no(self.port))
+        packed.append('\x00' * 6)
+        packed.append(loxi.generic_util.pack_list(self.queues))
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = queue_get_config_reply()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 21)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.port = util.unpack_port_no(reader)
+        reader.skip(6)
+        obj.queues = loxi.generic_util.unpack_list(reader, ofp.common.packet_queue.unpack)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.port != other.port: return False
+        if self.queues != other.queues: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("queue_get_config_reply {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("port = ");
+                q.text(util.pretty_port(self.port))
+                q.text(","); q.breakable()
+                q.text("queues = ");
+                q.pp(self.queues)
+            q.breakable()
+        q.text('}')
+
+message.subtypes[21] = queue_get_config_reply
+
+class queue_get_config_request(message):
+    version = 1
+    type = 20
+
+    def __init__(self, xid=None, port=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if port != None:
+            self.port = port
+        else:
+            self.port = 0
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(util.pack_port_no(self.port))
+        packed.append('\x00' * 2)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = queue_get_config_request()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 20)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        obj.port = util.unpack_port_no(reader)
+        reader.skip(2)
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.port != other.port: return False
+        return True
+
+    def pretty_print(self, q):
+        q.text("queue_get_config_request {")
+        with q.group():
+            with q.indent(2):
+                q.breakable()
+                q.text("xid = ");
+                if self.xid != None:
+                    q.text("%#x" % self.xid)
+                else:
+                    q.text('None')
+                q.text(","); q.breakable()
+                q.text("port = ");
+                q.text(util.pretty_port(self.port))
+            q.breakable()
+        q.text('}')
+
+message.subtypes[20] = queue_get_config_request
+
+class queue_op_failed_error_msg(error_msg):
+    version = 1
+    type = 1
+    err_type = 5
+
+    def __init__(self, xid=None, code=None, data=None):
+        if xid != None:
+            self.xid = xid
+        else:
+            self.xid = None
+        if code != None:
+            self.code = code
+        else:
+            self.code = 0
+        if data != None:
+            self.data = data
+        else:
+            self.data = ''
+        return
+
+    def pack(self):
+        packed = []
+        packed.append(struct.pack("!B", self.version))
+        packed.append(struct.pack("!B", self.type))
+        packed.append(struct.pack("!H", 0)) # placeholder for length at index 2
+        packed.append(struct.pack("!L", self.xid))
+        packed.append(struct.pack("!H", self.err_type))
+        packed.append(struct.pack("!H", self.code))
+        packed.append(self.data)
+        length = sum([len(x) for x in packed])
+        packed[2] = struct.pack("!H", length)
+        return ''.join(packed)
+
+    @staticmethod
+    def unpack(reader):
+        obj = queue_op_failed_error_msg()
+        _version = reader.read("!B")[0]
+        assert(_version == 1)
+        _type = reader.read("!B")[0]
+        assert(_type == 1)
+        _length = reader.read("!H")[0]
+        orig_reader = reader
+        reader = orig_reader.slice(_length, 4)
+        obj.xid = reader.read("!L")[0]
+        _err_type = reader.read("!H")[0]
+        assert(_err_type == 5)
+        obj.code = reader.read("!H")[0]
+        obj.data = str(reader.read_all())
+        return obj
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.xid != other.xid: return False
+        if self.code != other.code: return False
+        if self.data != other.data: return False
+        return True
+