diff --git a/hackfest/p4/setup.sh b/hackfest/p4/setup.sh
index 07fe22e6aea2341c50462010b4bfb55c4a657a47..195327a03fedafdc64a2d0dc34577766eda72a4f 100755
--- a/hackfest/p4/setup.sh
+++ b/hackfest/p4/setup.sh
@@ -4,5 +4,5 @@ export POD_NAME=$(kubectl get pods -n=tfs | grep device | awk '{print $1}')
 
 kubectl exec ${POD_NAME} -n=tfs -- mkdir /root/p4
 
-kubectl cp src/tests/netx22-p4/p4/p4info.txt tfs/${POD_NAME}:/root/p4
-kubectl cp src/tests/netx22-p4/p4/bmv2.json tfs/${POD_NAME}:/root/p4
+kubectl cp hackfest/p4/p4/p4info.txt tfs/${POD_NAME}:/root/p4
+kubectl cp hackfest/p4/p4/bmv2.json tfs/${POD_NAME}:/root/p4
diff --git a/hackfest/p4/tests/Objects.py b/hackfest/p4/tests/Objects.py
index 09b3aced843a198b7c963a34492a4fe2379c9123..c8b172244d714cd699ccc587e54c3751485a9a2e 100644
--- a/hackfest/p4/tests/Objects.py
+++ b/hackfest/p4/tests/Objects.py
@@ -1,4 +1,5 @@
 # Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
 # 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
@@ -42,6 +43,8 @@ PACKET_PORT_SAMPLE_TYPES = [
     KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED,
 ]
 
+# ----- Device Credentials and Settings --------------------------------------------------------------------------------
+
 
 # ----- Devices --------------------------------------------------------------------------------------------------------
 
@@ -54,7 +57,7 @@ DEVICE_SW1                  = json_device_p4_disabled(DEVICE_SW1_UUID)
 
 DEVICE_SW1_DPID             = 1
 DEVICE_SW1_NAME             = DEVICE_SW1_UUID
-DEVICE_SW1_IP_ADDR          = '10.0.2.10'
+DEVICE_SW1_IP_ADDR          = 'localhost'
 DEVICE_SW1_PORT             = '50001'
 DEVICE_SW1_VENDOR           = 'Open Networking Foundation'
 DEVICE_SW1_HW_VER           = 'BMv2 simple_switch'
@@ -78,9 +81,38 @@ DEVICE_SW1_CONNECT_RULES    = json_device_connect_rules(
     }
 )
 
+DEVICE_SW2_UUID             = 'SW2'
+DEVICE_SW2_TIMEOUT          = 60
+DEVICE_SW2_ID               = json_device_id(DEVICE_SW2_UUID)
+DEVICE_SW2                  = json_device_p4_disabled(DEVICE_SW2_UUID)
 
-################################## TABLE ENTRIES ##################################
+DEVICE_SW2_DPID             = 1
+DEVICE_SW2_NAME             = DEVICE_SW2_UUID
+DEVICE_SW2_IP_ADDR          = 'localhost'
+DEVICE_SW2_PORT             = '50002'
+DEVICE_SW2_VENDOR           = 'Open Networking Foundation'
+DEVICE_SW2_HW_VER           = 'BMv2 simple_switch'
+DEVICE_SW2_SW_VER           = 'Stratum'
 
+DEVICE_SW2_BIN_PATH         = '/root/p4/bmv2.json'
+DEVICE_SW2_INFO_PATH        = '/root/p4/p4info.txt'
+
+DEVICE_SW2_CONNECT_RULES    = json_device_connect_rules(
+    DEVICE_SW2_IP_ADDR,
+    DEVICE_SW2_PORT,
+    {
+        'id':       DEVICE_SW2_DPID,
+        'name':     DEVICE_SW2_NAME,
+        'vendor':   DEVICE_SW2_VENDOR,
+        'hw_ver':   DEVICE_SW2_HW_VER,
+        'sw_ver':   DEVICE_SW2_SW_VER,
+        'timeout':  DEVICE_SW2_TIMEOUT,
+        'p4bin':    DEVICE_SW2_BIN_PATH,
+        'p4info':   DEVICE_SW2_INFO_PATH
+    }
+)
+
+################################## TABLE ENTRIES ##################################
 
 DEVICE_SW1_CONFIG_TABLE_ENTRIES = [
     json_config_rule_set(
@@ -123,6 +155,8 @@ DEVICE_SW1_CONFIG_TABLE_ENTRIES = [
     )
 ]
 
+DEVICE_SW2_CONFIG_TABLE_ENTRIES = DEVICE_SW1_CONFIG_TABLE_ENTRIES 
+
 
 """
 DEVICE_SW1_CONFIG_TABLE_ENTRIES = [
@@ -171,7 +205,6 @@ DEVICE_SW1_CONFIG_TABLE_ENTRIES = [
 
 ################################## TABLE DECONF ##################################
 
-
 DEVICE_SW1_DECONF_TABLE_ENTRIES = [
     json_config_rule_delete(
         'table',
@@ -213,6 +246,7 @@ DEVICE_SW1_DECONF_TABLE_ENTRIES = [
     )
 ]
 
+DEVICE_SW2_DECONF_TABLE_ENTRIES = DEVICE_SW1_DECONF_TABLE_ENTRIES 
 
 
 """
@@ -271,6 +305,7 @@ TOPOLOGIES = [TOPOLOGY]
 
 DEVICES = [
     (DEVICE_SW1, DEVICE_SW1_CONNECT_RULES, DEVICE_SW1_CONFIG_TABLE_ENTRIES, DEVICE_SW1_DECONF_TABLE_ENTRIES),
+    (DEVICE_SW2, DEVICE_SW2_CONNECT_RULES, DEVICE_SW2_CONFIG_TABLE_ENTRIES, DEVICE_SW2_DECONF_TABLE_ENTRIES),
 ]
 
 LINKS = []
diff --git a/hackfest/p4/tests/test_functional_cleanup.py b/hackfest/p4/tests/test_functional_cleanup.py
index 32f716f1c2287b11bae3610022d64659d82ba73d..ccbcb9843a03bbf095743af0753da3fe8af3bfce 100644
--- a/hackfest/p4/tests/test_functional_cleanup.py
+++ b/hackfest/p4/tests/test_functional_cleanup.py
@@ -54,8 +54,8 @@ def test_scenario_cleanup(
         device_client.DeleteDevice(DeviceId(**device_id))
         #expected_events.append(('DeviceEvent', EVENT_REMOVE, json_device_id(device_uuid)))
 
-        response = context_client.ListDevices(Empty())
-        assert len(response.devices) == 0
+    response = context_client.ListDevices(Empty())
+    assert len(response.devices) == 0
 
     # ----- Delete Topologies and Validate Collected Events ------------------------------------------------------------
     for topology in TOPOLOGIES:
diff --git a/src/device/service/drivers/p4/p4_driver.py b/src/device/service/drivers/p4/p4_driver.py
index 069c07ce40e43192b74519b2175e7e10c638cd20..b8ff795fbd9466874b07f1f752fce682ea741111 100644
--- a/src/device/service/drivers/p4/p4_driver.py
+++ b/src/device/service/drivers/p4/p4_driver.py
@@ -28,7 +28,7 @@ from .p4_common import matches_ipv4, matches_ipv6, valid_port,\
     P4_ATTR_DEV_P4BIN, P4_ATTR_DEV_P4INFO, P4_ATTR_DEV_TIMEOUT,\
     P4_VAL_DEF_VENDOR, P4_VAL_DEF_HW_VER, P4_VAL_DEF_SW_VER,\
     P4_VAL_DEF_TIMEOUT
-from .p4_manager import P4Manager, get_api_version, KEY_TABLE,\
+from .p4_manager import P4Manager, KEY_TABLE,\
     KEY_ACTION_PROFILE, KEY_COUNTER, KEY_DIR_COUNTER, KEY_METER, KEY_DIR_METER,\
     KEY_CTL_PKT_METADATA
 from .p4_client import WriteOperation
@@ -127,8 +127,7 @@ class P4Driver(_Driver):
             except Exception as ex:  # pylint: disable=broad-except
                 raise Exception(ex) from ex
 
-            LOGGER.info("\tConnected via P4Runtime version %s",
-                        get_api_version())
+            LOGGER.info("\tConnected via P4Runtime")
             self.__started.set()
 
             return True
diff --git a/src/device/service/drivers/p4/p4_manager.py b/src/device/service/drivers/p4/p4_manager.py
index 65f8602ea30fa2d8cd06b09655ee4ee63d045a97..178487250ea3a5652690fb39f1631a0133aec4e3 100644
--- a/src/device/service/drivers/p4/p4_manager.py
+++ b/src/device/service/drivers/p4/p4_manager.py
@@ -55,7 +55,7 @@ LOGGER = logging.getLogger(__name__)
 CONTEXT = Context()
 
 # Global P4Runtime client
-CLIENT = None
+CLIENTS = {}
 
 # Constant P4 entities
 KEY_TABLE = "table"
@@ -76,25 +76,6 @@ def get_context():
     """
     return CONTEXT
 
-
-def get_client():
-    """
-    Return P4 client.
-
-    :return: P4Runtime client object
-    """
-    return CLIENT
-
-
-def get_api_version():
-    """
-    Get the supported P4Runtime API version.
-
-    :return: API version
-    """
-    return CLIENT.api_version()
-
-
 def get_table_type(table):
     """
     Assess the type of P4 table based upon the matching scheme.
@@ -136,171 +117,28 @@ def match_type_to_str(match_type):
     return None
 
 
-def insert_table_entry_exact(
-        table_name, match_map, action_name, action_params, metadata,
-        cnt_pkt=-1, cnt_byte=-1):
-    """
-    Insert an entry into an exact match table.
-
-    :param table_name: P4 table name
-    :param match_map: Map of match operations
-    :param action_name: Action name
-    :param action_params: Map of action parameters
-    :param metadata: table metadata
-    :param cnt_pkt: packet count
-    :param cnt_byte: byte count
-    :return: inserted entry
-    """
-    assert match_map, "Table entry without match operations is not accepted"
-    assert action_name, "Table entry without action is not accepted"
-
-    table_entry = TableEntry(table_name)(action=action_name)
-
-    for match_k, match_v in match_map.items():
-        table_entry.match[match_k] = match_v
-
-    for action_k, action_v in action_params.items():
-        table_entry.action[action_k] = action_v
-
-    if metadata:
-        table_entry.metadata = metadata
-
-    if cnt_pkt > 0:
-        table_entry.counter_data.packet_count = cnt_pkt
-
-    if cnt_byte > 0:
-        table_entry.counter_data.byte_count = cnt_byte
-
-    ex_msg = ""
-    try:
-        table_entry.insert()
-        LOGGER.info("Inserted exact table entry: %s", table_entry)
-    except (P4RuntimeException, P4RuntimeWriteException) as ex:
-        raise P4RuntimeException from ex
-
-    # Table entry exists, needs to be modified
-    if "ALREADY_EXISTS" in ex_msg:
-        table_entry.modify()
-        LOGGER.info("Updated exact table entry: %s", table_entry)
-
-    return table_entry
-
-
-def insert_table_entry_ternary(
-        table_name, match_map, action_name, action_params, metadata,
-        priority, cnt_pkt=-1, cnt_byte=-1):
-    """
-    Insert an entry into a ternary match table.
-
-    :param table_name: P4 table name
-    :param match_map: Map of match operations
-    :param action_name: Action name
-    :param action_params: Map of action parameters
-    :param metadata: table metadata
-    :param priority: entry priority
-    :param cnt_pkt: packet count
-    :param cnt_byte: byte count
-    :return: inserted entry
-    """
-    assert match_map, "Table entry without match operations is not accepted"
-    assert action_name, "Table entry without action is not accepted"
-
-    table_entry = TableEntry(table_name)(action=action_name)
-
-    for match_k, match_v in match_map.items():
-        table_entry.match[match_k] = match_v
-
-    for action_k, action_v in action_params.items():
-        table_entry.action[action_k] = action_v
-
-    table_entry.priority = priority
-
-    if metadata:
-        table_entry.metadata = metadata
-
-    if cnt_pkt > 0:
-        table_entry.counter_data.packet_count = cnt_pkt
-
-    if cnt_byte > 0:
-        table_entry.counter_data.byte_count = cnt_byte
-
-    ex_msg = ""
-    try:
-        table_entry.insert()
-        LOGGER.info("Inserted ternary table entry: %s", table_entry)
-    except (P4RuntimeException, P4RuntimeWriteException) as ex:
-        raise P4RuntimeException from ex
-
-    # Table entry exists, needs to be modified
-    if "ALREADY_EXISTS" in ex_msg:
-        table_entry.modify()
-        LOGGER.info("Updated ternary table entry: %s", table_entry)
-
-    return table_entry
-
-
-def insert_table_entry_range(
-        table_name, match_map, action_name, action_params, metadata,
-        priority, cnt_pkt=-1, cnt_byte=-1):  # pylint: disable=unused-argument
-    """
-    Insert an entry into a range match table.
-
-    :param table_name: P4 table name
-    :param match_map: Map of match operations
-    :param action_name: Action name
-    :param action_params: Map of action parameters
-    :param metadata: table metadata
-    :param priority: entry priority
-    :param cnt_pkt: packet count
-    :param cnt_byte: byte count
-    :return: inserted entry
-    """
-    assert match_map, "Table entry without match operations is not accepted"
-    assert action_name, "Table entry without action is not accepted"
-
-    raise NotImplementedError(
-        "Range-based table insertion not implemented yet")
-
-
-def insert_table_entry_optional(
-        table_name, match_map, action_name, action_params, metadata,
-        priority, cnt_pkt=-1, cnt_byte=-1):  # pylint: disable=unused-argument
-    """
-    Insert an entry into an optional match table.
-
-    :param table_name: P4 table name
-    :param match_map: Map of match operations
-    :param action_name: Action name
-    :param action_params: Map of action parameters
-    :param metadata: table metadata
-    :param priority: entry priority
-    :param cnt_pkt: packet count
-    :param cnt_byte: byte count
-    :return: inserted entry
-    """
-    assert match_map, "Table entry without match operations is not accepted"
-    assert action_name, "Table entry without action is not accepted"
-
-    raise NotImplementedError(
-        "Optional-based table insertion not implemented yet")
-
 
 class P4Manager:
     """
     Class to manage the runtime entries of a P4 pipeline.
     """
+    local_client = None
+    key_id = None
 
     def __init__(self, device_id: int, ip_address: str, port: int,
                  election_id: tuple, role_name=None, ssl_options=None):
-        global CLIENT
+        global CLIENTS
 
         self.__id = device_id
         self.__ip_address = ip_address
         self.__port = int(port)
         self.__endpoint = f"{self.__ip_address}:{self.__port}"
-        CLIENT = P4RuntimeClient(
+        self.key_id = ip_address+str(port)
+        CLIENTS[self.key_id] = P4RuntimeClient(
             self.__id, self.__endpoint, election_id, role_name, ssl_options)
         self.__p4info = None
+        
+        self.local_client = CLIENTS[self.key_id]
 
         # Internal memory for whitebox management
         # | -> P4 entities
@@ -339,27 +177,27 @@ class P4Manager:
         # Forwarding pipeline is only set iff both files are present
         if p4bin_path and p4info_path:
             try:
-                CLIENT.set_fwd_pipe_config(p4info_path, p4bin_path)
+                self.local_client.set_fwd_pipe_config(p4info_path, p4bin_path)
             except FileNotFoundError as ex:
                 LOGGER.critical(ex)
-                CLIENT.tear_down()
+                self.local_client.tear_down()
                 raise FileNotFoundError(ex) from ex
             except P4RuntimeException as ex:
                 LOGGER.critical("Error when setting config")
                 LOGGER.critical(ex)
-                CLIENT.tear_down()
+                self.local_client.tear_down()
                 raise P4RuntimeException(ex) from ex
             except Exception as ex:  # pylint: disable=broad-except
                 LOGGER.critical("Error when setting config")
-                CLIENT.tear_down()
+                self.local_client.tear_down()
                 raise Exception(ex) from ex
 
         try:
-            self.__p4info = CLIENT.get_p4info()
+            self.__p4info = self.local_client.get_p4info()
         except P4RuntimeException as ex:
             LOGGER.critical("Error when retrieving P4Info")
             LOGGER.critical(ex)
-            CLIENT.tear_down()
+            self.local_client.tear_down()
             raise P4RuntimeException(ex) from ex
 
         CONTEXT.set_p4info(self.__p4info)
@@ -375,14 +213,15 @@ class P4Manager:
 
         :return: void
         """
-        global CLIENT
+        global CLIENTS
 
         # gRPC client must already be instantiated
-        assert CLIENT
+        assert self.local_client
 
         # Trigger connection tear down with the P4Runtime server
-        CLIENT.tear_down()
-        CLIENT = None
+        self.local_client.tear_down()
+        # Remove client entry from global dictionary
+        CLIENTS.pop(self.key_id)
         self.__clear()
         LOGGER.info("P4Runtime manager stopped")
 
@@ -723,7 +562,7 @@ class P4Manager:
 
         try:
             for count, table_entry in enumerate(
-                    TableEntry(table_name)(action=action_name).read()):
+                    TableEntry(self.local_client, table_name)(action=action_name).read()):
                 LOGGER.debug(
                     "Table %s - Entry %d\n%s", table_name, count, table_entry)
                 self.table_entries[table_name].append(table_entry)
@@ -856,6 +695,154 @@ class P4Manager:
             )
         return None
 
+    def insert_table_entry_exact(self,
+            table_name, match_map, action_name, action_params, metadata,
+            cnt_pkt=-1, cnt_byte=-1):
+        """
+        Insert an entry into an exact match table.
+    
+        :param table_name: P4 table name
+        :param match_map: Map of match operations
+        :param action_name: Action name
+        :param action_params: Map of action parameters
+        :param metadata: table metadata
+        :param cnt_pkt: packet count
+        :param cnt_byte: byte count
+        :return: inserted entry
+        """
+        assert match_map, "Table entry without match operations is not accepted"
+        assert action_name, "Table entry without action is not accepted"
+    
+        table_entry = TableEntry(self.local_client, table_name)(action=action_name)
+    
+        for match_k, match_v in match_map.items():
+            table_entry.match[match_k] = match_v
+    
+        for action_k, action_v in action_params.items():
+            table_entry.action[action_k] = action_v
+    
+        if metadata:
+            table_entry.metadata = metadata
+    
+        if cnt_pkt > 0:
+            table_entry.counter_data.packet_count = cnt_pkt
+    
+        if cnt_byte > 0:
+            table_entry.counter_data.byte_count = cnt_byte
+    
+        ex_msg = ""
+        try:
+            table_entry.insert()
+            LOGGER.info("Inserted exact table entry: %s", table_entry)
+        except (P4RuntimeException, P4RuntimeWriteException) as ex:
+            raise P4RuntimeException from ex
+    
+        # Table entry exists, needs to be modified
+        if "ALREADY_EXISTS" in ex_msg:
+            table_entry.modify()
+            LOGGER.info("Updated exact table entry: %s", table_entry)
+    
+        return table_entry
+    
+    
+    def insert_table_entry_ternary(self,
+            table_name, match_map, action_name, action_params, metadata,
+            priority, cnt_pkt=-1, cnt_byte=-1):
+        """
+        Insert an entry into a ternary match table.
+    
+        :param table_name: P4 table name
+        :param match_map: Map of match operations
+        :param action_name: Action name
+        :param action_params: Map of action parameters
+        :param metadata: table metadata
+        :param priority: entry priority
+        :param cnt_pkt: packet count
+        :param cnt_byte: byte count
+        :return: inserted entry
+        """
+        assert match_map, "Table entry without match operations is not accepted"
+        assert action_name, "Table entry without action is not accepted"
+    
+        table_entry = TableEntry(self.local_client, table_name)(action=action_name)
+    
+        for match_k, match_v in match_map.items():
+            table_entry.match[match_k] = match_v
+    
+        for action_k, action_v in action_params.items():
+            table_entry.action[action_k] = action_v
+    
+        table_entry.priority = priority
+    
+        if metadata:
+            table_entry.metadata = metadata
+    
+        if cnt_pkt > 0:
+            table_entry.counter_data.packet_count = cnt_pkt
+    
+        if cnt_byte > 0:
+            table_entry.counter_data.byte_count = cnt_byte
+    
+        ex_msg = ""
+        try:
+            table_entry.insert()
+            LOGGER.info("Inserted ternary table entry: %s", table_entry)
+        except (P4RuntimeException, P4RuntimeWriteException) as ex:
+            raise P4RuntimeException from ex
+    
+        # Table entry exists, needs to be modified
+        if "ALREADY_EXISTS" in ex_msg:
+            table_entry.modify()
+            LOGGER.info("Updated ternary table entry: %s", table_entry)
+    
+        return table_entry
+    
+    
+    def insert_table_entry_range(self,
+            table_name, match_map, action_name, action_params, metadata,
+            priority, cnt_pkt=-1, cnt_byte=-1):  # pylint: disable=unused-argument
+        """
+        Insert an entry into a range match table.
+    
+        :param table_name: P4 table name
+        :param match_map: Map of match operations
+        :param action_name: Action name
+        :param action_params: Map of action parameters
+        :param metadata: table metadata
+        :param priority: entry priority
+        :param cnt_pkt: packet count
+        :param cnt_byte: byte count
+        :return: inserted entry
+        """
+        assert match_map, "Table entry without match operations is not accepted"
+        assert action_name, "Table entry without action is not accepted"
+    
+        raise NotImplementedError(
+            "Range-based table insertion not implemented yet")
+    
+    
+    def insert_table_entry_optional(self,
+            table_name, match_map, action_name, action_params, metadata,
+            priority, cnt_pkt=-1, cnt_byte=-1):  # pylint: disable=unused-argument
+        """
+        Insert an entry into an optional match table.
+    
+        :param table_name: P4 table name
+        :param match_map: Map of match operations
+        :param action_name: Action name
+        :param action_params: Map of action parameters
+        :param metadata: table metadata
+        :param priority: entry priority
+        :param cnt_pkt: packet count
+        :param cnt_byte: byte count
+        :return: inserted entry
+        """
+        assert match_map, "Table entry without match operations is not accepted"
+        assert action_name, "Table entry without action is not accepted"
+    
+        raise NotImplementedError(
+            "Optional-based table insertion not implemented yet")
+
     def insert_table_entry(self, table_name,
                            match_map, action_name, action_params,
                            priority, metadata=None, cnt_pkt=-1, cnt_byte=-1):
@@ -889,26 +876,26 @@ class P4Manager:
 
         # Exact match is supported
         if get_table_type(table) == p4info_pb2.MatchField.EXACT:
-            return insert_table_entry_exact(
+            return self.insert_table_entry_exact(
                 table_name, match_map, action_name, action_params, metadata,
                 cnt_pkt, cnt_byte)
 
         # Ternary and LPM matches are supported
         if get_table_type(table) in \
                 [p4info_pb2.MatchField.TERNARY, p4info_pb2.MatchField.LPM]:
-            return insert_table_entry_ternary(
+            return self.insert_table_entry_ternary(
                 table_name, match_map, action_name, action_params, metadata,
                 priority, cnt_pkt, cnt_byte)
 
         # TODO: Cover RANGE match  # pylint: disable=W0511
         if get_table_type(table) == p4info_pb2.MatchField.RANGE:
-            return insert_table_entry_range(
+            return self.insert_table_entry_range(
                 table_name, match_map, action_name, action_params, metadata,
                 priority, cnt_pkt, cnt_byte)
 
         # TODO: Cover OPTIONAL match  # pylint: disable=W0511
         if get_table_type(table) == p4info_pb2.MatchField.OPTIONAL:
-            return insert_table_entry_optional(
+            return self.insert_table_entry_optional(
                 table_name, match_map, action_name, action_params, metadata,
                 priority, cnt_pkt, cnt_byte)
 
@@ -935,7 +922,7 @@ class P4Manager:
             LOGGER.error(msg)
             raise UserError(msg)
 
-        table_entry = TableEntry(table_name)(action=action_name)
+        table_entry = TableEntry(self.local_client, table_name)(action=action_name)
 
         for match_k, match_v in match_map.items():
             table_entry.match[match_k] = match_v
@@ -979,7 +966,7 @@ class P4Manager:
             LOGGER.error(msg)
             raise UserError(msg)
 
-        TableEntry(table_name).read(function=lambda x: x.delete())
+        TableEntry(self.local_client, table_name).read(function=lambda x: x.delete())
         LOGGER.info("Deleted all entries from table: %s", table_name)
 
     def print_table_entries_spec(self, table_name):
@@ -1179,7 +1166,7 @@ class P4Manager:
         self.counter_entries[cnt_name] = []
 
         try:
-            for count, cnt_entry in enumerate(CounterEntry(cnt_name).read()):
+            for count, cnt_entry in enumerate(CounterEntry(self.local_client, cnt_name).read()):
                 LOGGER.debug(
                     "Counter %s - Entry %d\n%s", cnt_name, count, cnt_entry)
                 self.counter_entries[cnt_name].append(cnt_entry)
@@ -1298,7 +1285,7 @@ class P4Manager:
         assert cnt, \
             "P4 pipeline does not implement counter " + cnt_name
 
-        cnt_entry = CounterEntry(cnt_name)
+        cnt_entry = CounterEntry(self.local_client, cnt_name)
 
         if index:
             cnt_entry.index = index
@@ -1325,7 +1312,7 @@ class P4Manager:
         assert cnt, \
             "P4 pipeline does not implement counter " + cnt_name
 
-        cnt_entry = CounterEntry(cnt_name)
+        cnt_entry = CounterEntry(self.local_client, cnt_name)
         cnt_entry.clear_data()
         LOGGER.info("Cleared data of counter entry: %s", cnt_entry)
 
@@ -1394,7 +1381,7 @@ class P4Manager:
 
         try:
             for count, d_cnt_entry in enumerate(
-                    DirectCounterEntry(d_cnt_name).read()):
+                    DirectCounterEntry(self.local_client, d_cnt_name).read()):
                 LOGGER.debug(
                     "Direct counter %s - Entry %d\n%s",
                     d_cnt_name, count, d_cnt_entry)
@@ -1530,7 +1517,7 @@ class P4Manager:
         assert match_map,\
             "Direct counter entry without match operations is not accepted"
 
-        d_cnt_entry = DirectCounterEntry(d_cnt_name)
+        d_cnt_entry = DirectCounterEntry(self.local_client, d_cnt_name)
 
         for match_k, match_v in match_map.items():
             d_cnt_entry.table_entry.match[match_k] = match_v
@@ -1559,7 +1546,7 @@ class P4Manager:
         assert d_cnt, \
             "P4 pipeline does not implement direct counter " + d_cnt_name
 
-        d_cnt_entry = DirectCounterEntry(d_cnt_name)
+        d_cnt_entry = DirectCounterEntry(self.local_client, d_cnt_name)
         d_cnt_entry.clear_data()
         LOGGER.info("Cleared direct counter entry: %s", d_cnt_entry)
 
@@ -1627,7 +1614,7 @@ class P4Manager:
         self.meter_entries[meter_name] = []
 
         try:
-            for count, meter_entry in enumerate(MeterEntry(meter_name).read()):
+            for count, meter_entry in enumerate(MeterEntry(self.local_client, meter_name).read()):
                 LOGGER.debug(
                     "Meter %s - Entry %d\n%s", meter_name, count, meter_entry)
                 self.meter_entries[meter_name].append(meter_entry)
@@ -1756,7 +1743,7 @@ class P4Manager:
         assert meter, \
             "P4 pipeline does not implement meter " + meter_name
 
-        meter_entry = MeterEntry(meter_name)
+        meter_entry = MeterEntry(self.local_client, meter_name)
 
         if index:
             meter_entry.index = index
@@ -1789,7 +1776,7 @@ class P4Manager:
         assert meter, \
             "P4 pipeline does not implement meter " + meter_name
 
-        meter_entry = MeterEntry(meter_name)
+        meter_entry = MeterEntry(self.local_client, meter_name)
         meter_entry.clear_config()
         LOGGER.info("Cleared meter entry: %s", meter_entry)
 
@@ -1858,7 +1845,7 @@ class P4Manager:
 
         try:
             for count, d_meter_entry in enumerate(
-                    MeterEntry(d_meter_name).read()):
+                    MeterEntry(self.local_client, d_meter_name).read()):
                 LOGGER.debug(
                     "Direct meter %s - Entry %d\n%s",
                     d_meter_name, count, d_meter_entry)
@@ -1998,7 +1985,7 @@ class P4Manager:
         assert match_map,\
             "Direct meter entry without match operations is not accepted"
 
-        d_meter_entry = DirectMeterEntry(d_meter_name)
+        d_meter_entry = DirectMeterEntry(self.local_client, d_meter_name)
 
         for match_k, match_v in match_map.items():
             d_meter_entry.table_entry.match[match_k] = match_v
@@ -2031,7 +2018,7 @@ class P4Manager:
         assert d_meter, \
             "P4 pipeline does not implement direct meter " + d_meter_name
 
-        d_meter_entry = DirectMeterEntry(d_meter_name)
+        d_meter_entry = DirectMeterEntry(self.local_client, d_meter_name)
         d_meter_entry.clear_config()
         LOGGER.info("Cleared direct meter entry: %s", d_meter_entry)
 
@@ -2100,7 +2087,7 @@ class P4Manager:
 
         try:
             for count, ap_entry in enumerate(
-                    ActionProfileMember(ap_name).read()):
+                    ActionProfileMember(self.local_client, ap_name).read()):
                 LOGGER.debug(
                     "Action profile member %s - Entry %d\n%s",
                     ap_name, count, ap_entry)
@@ -2230,7 +2217,7 @@ class P4Manager:
         assert act_p, \
             "P4 pipeline does not implement action profile " + ap_name
 
-        ap_member_entry = ActionProfileMember(ap_name)(
+        ap_member_entry = ActionProfileMember(self.local_client, ap_name)(
             member_id=member_id, action=action_name)
 
         for action_k, action_v in action_params.items():
@@ -2267,7 +2254,7 @@ class P4Manager:
         assert act_p, \
             "P4 pipeline does not implement action profile " + ap_name
 
-        ap_member_entry = ActionProfileMember(ap_name)(
+        ap_member_entry = ActionProfileMember(self.local_client, ap_name)(
             member_id=member_id, action=action_name)
         ap_member_entry.delete()
         LOGGER.info("Deleted action profile member entry: %s", ap_member_entry)
@@ -2364,7 +2351,7 @@ class P4Manager:
 
         try:
             for count, ap_entry in enumerate(
-                    ActionProfileGroup(ap_name).read()):
+                    ActionProfileGroup(self.local_client, ap_name).read()):
                 LOGGER.debug("Action profile group %s - Entry %d\n%s",
                              ap_name, count, ap_entry)
                 self.action_profile_groups[ap_name].append(ap_entry)
@@ -2483,7 +2470,7 @@ class P4Manager:
         assert ap, \
             "P4 pipeline does not implement action profile " + ap_name
 
-        ap_group_entry = ActionProfileGroup(ap_name)(group_id=group_id)
+        ap_group_entry = ActionProfileGroup(self.local_client, ap_name)(group_id=group_id)
 
         if members:
             for m in members:
@@ -2519,7 +2506,7 @@ class P4Manager:
         assert ap, \
             "P4 pipeline does not implement action profile " + ap_name
 
-        ap_group_entry = ActionProfileGroup(ap_name)(group_id=group_id)
+        ap_group_entry = ActionProfileGroup(self.local_client, ap_name)(group_id=group_id)
         ap_group_entry.delete()
         LOGGER.info("Deleted action profile group entry: %s", ap_group_entry)
 
@@ -2537,7 +2524,7 @@ class P4Manager:
         assert ap, \
             "P4 pipeline does not implement action profile " + ap_name
 
-        ap_group_entry = ActionProfileGroup(ap_name)(group_id=group_id)
+        ap_group_entry = ActionProfileGroup(self.local_client, ap_name)(group_id=group_id)
         ap_group_entry.clear()
         LOGGER.info("Cleared action profile group entry: %s", ap_group_entry)
 
@@ -2631,7 +2618,7 @@ class P4Manager:
         self.multicast_groups[group_id] = None
 
         try:
-            mcast_group = MulticastGroupEntry(group_id).read()
+            mcast_group = MulticastGroupEntry(self.local_client, group_id).read()
             LOGGER.debug("Multicast group %d\n%s", group_id, mcast_group)
             self.multicast_groups[group_id] = mcast_group
             return self.multicast_groups[group_id]
@@ -2724,7 +2711,7 @@ class P4Manager:
         assert ports, \
             "No multicast group ports are provided"
 
-        mcast_group = MulticastGroupEntry(group_id)
+        mcast_group = MulticastGroupEntry(self.local_client, group_id)
         for p in ports:
             mcast_group.add(p, 1)
 
@@ -2756,7 +2743,7 @@ class P4Manager:
         assert group_id > 0, \
             "Multicast group " + group_id + " must be > 0"
 
-        mcast_group = MulticastGroupEntry(group_id)
+        mcast_group = MulticastGroupEntry(self.local_client, group_id)
         mcast_group.delete()
 
         if group_id in self.multicast_groups:
@@ -2772,7 +2759,7 @@ class P4Manager:
 
         :return: void
         """
-        for mcast_group in MulticastGroupEntry().read():
+        for mcast_group in MulticastGroupEntry(self.local_client).read():
             gid = mcast_group.group_id
             mcast_group.delete()
             del self.multicast_groups[gid]
@@ -2828,7 +2815,7 @@ class P4Manager:
         self.clone_session_entries[session_id] = None
 
         try:
-            session = CloneSessionEntry(session_id).read()
+            session = CloneSessionEntry(self.local_client, session_id).read()
             LOGGER.debug("Clone session %d\n%s", session_id, session)
             self.clone_session_entries[session_id] = session
             return self.clone_session_entries[session_id]
@@ -2923,7 +2910,7 @@ class P4Manager:
         assert ports, \
             "No clone session ports are provided"
 
-        session = CloneSessionEntry(session_id)
+        session = CloneSessionEntry(self.local_client, session_id)
         for p in ports:
             session.add(p, 1)
 
@@ -2955,7 +2942,7 @@ class P4Manager:
         assert session_id > 0, \
             "Clone session " + session_id + " must be > 0"
 
-        session = CloneSessionEntry(session_id)
+        session = CloneSessionEntry(self.local_client, session_id)
         session.delete()
 
         if session_id in self.clone_session_entries:
@@ -2971,7 +2958,7 @@ class P4Manager:
 
         :return: void
         """
-        for e in CloneSessionEntry().read():
+        for e in CloneSessionEntry(self.local_client).read():
             sid = e.session_id
             e.delete()
             del self.clone_session_entries[sid]
@@ -3052,7 +3039,7 @@ class P4Manager:
                            "No controller packet metadata in the pipeline\n")
             return None
 
-        packet_in = PacketOut()
+        packet_in = PacketIn(self.local_client)
         packet_in.payload = payload
         if metadata:
             for name, value in metadata.items():
@@ -3090,7 +3077,7 @@ class P4Manager:
         _t = Thread(target=_sniff_packet, args=(captured_packet,))
         _t.start()
         # P4Runtime client sends the packet to the switch
-        CLIENT.stream_in_q["packet"].put(packet_in)
+        self.local_client.stream_in_q["packet"].put(packet_in)
         _t.join()
         LOGGER.info("Packet-in sent: %s", packet_in)
 
@@ -3111,7 +3098,7 @@ class P4Manager:
                            "No controller packet metadata in the pipeline\n")
             return None
 
-        packet_out = PacketOut()
+        packet_out = PacketOut(self.local_client)
         packet_out.payload = payload
         if metadata:
             for name, value in metadata.items():
@@ -3654,12 +3641,14 @@ class _EntityBase:
     """
     Basic entity.
     """
+    local_client = None
 
-    def __init__(self, entity_type, p4runtime_cls, modify_only=False):
+    def __init__(self, p4_client, entity_type, p4runtime_cls, modify_only=False):
         self._init = False
         self._entity_type = entity_type
         self._entry = p4runtime_cls()
         self._modify_only = modify_only
+        self.local_client = p4_client
 
     def __dir__(self):
         d = ["msg", "read"]
@@ -3696,7 +3685,7 @@ class _EntityBase:
         update = p4runtime_pb2.Update()
         update.type = type_
         getattr(update.entity, self._entity_type.name).CopyFrom(self._entry)
-        CLIENT.write_update(update)
+        self.local_client.write_update(update)
 
     def insert(self):
         """
@@ -3747,7 +3736,7 @@ class _EntityBase:
         entity = p4runtime_pb2.Entity()
         getattr(entity, self._entity_type.name).CopyFrom(self._entry)
 
-        iterator = CLIENT.read_one(entity)
+        iterator = self.local_client.read_one(entity)
 
         # Cannot use a (simpler) generator here as we need to
         # decorate __next__ with @parse_p4runtime_error.
@@ -3794,9 +3783,9 @@ class _P4EntityBase(_EntityBase):
     Basic P4 entity.
     """
 
-    def __init__(self, p4_type, entity_type, p4runtime_cls, name=None,
+    def __init__(self, p4_client, p4_type, entity_type, p4runtime_cls, name=None,
                  modify_only=False):
-        super().__init__(entity_type, p4runtime_cls, modify_only)
+        super().__init__(p4_client, entity_type, p4runtime_cls, modify_only)
         self._p4_type = p4_type
         if name is None:
             raise UserError(
@@ -3825,8 +3814,8 @@ class ActionProfileMember(_P4EntityBase):
     P4 action profile member.
     """
 
-    def __init__(self, action_profile_name=None):
-        super().__init__(
+    def __init__(self, p4_client, action_profile_name=None):
+        super().__init__( p4_client,
             P4Type.action_profile, P4RuntimeEntity.action_profile_member,
             p4runtime_pb2.ActionProfileMember, action_profile_name)
         self.member_id = 0
@@ -3991,8 +3980,8 @@ class ActionProfileGroup(_P4EntityBase):
     P4 action profile group.
     """
 
-    def __init__(self, action_profile_name=None):
-        super().__init__(
+    def __init__(self, p4_client, action_profile_name=None):
+        super().__init__( p4_client,
             P4Type.action_profile, P4RuntimeEntity.action_profile_group,
             p4runtime_pb2.ActionProfileGroup, action_profile_name)
         self.group_id = 0
@@ -4554,8 +4543,8 @@ class TableEntry(_P4EntityBase):
             "oneshot": cls._ActionSpecType.ONESHOT,
         }.get(name, None)
 
-    def __init__(self, table_name=None):
-        super().__init__(
+    def __init__(self, p4_client, table_name=None):
+        super().__init__(p4_client,
             P4Type.table, P4RuntimeEntity.table_entry,
             p4runtime_pb2.TableEntry, table_name)
         self.match = MatchKey(table_name, self._info.match_fields)
@@ -4996,8 +4985,8 @@ class _CounterEntryBase(_P4EntityBase):
     Basic P4 counter entry.
     """
 
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
+    def __init__(self, p4_client, *args, **kwargs):
+        super().__init__(p4_client, *args, **kwargs)
         self._counter_type = self._info.spec.unit
         self.packet_count = -1
         self.byte_count = -1
@@ -5065,8 +5054,8 @@ class CounterEntry(_CounterEntryBase):
     P4 counter entry.
     """
 
-    def __init__(self, counter_name=None):
-        super().__init__(
+    def __init__(self, p4_client, counter_name=None):
+        super().__init__( p4_client,
             P4Type.counter, P4RuntimeEntity.counter_entry,
             p4runtime_pb2.CounterEntry, counter_name,
             modify_only=True)
@@ -5126,10 +5115,11 @@ To write to the counter, use <self>.modify
 class DirectCounterEntry(_CounterEntryBase):
     """
     Direct P4 counter entry.
-    """
+    """ 
+    local_client = None
 
-    def __init__(self, direct_counter_name=None):
-        super().__init__(
+    def __init__(self, p4_client, direct_counter_name=None):
+        super().__init__( p4_client, 
             P4Type.direct_counter, P4RuntimeEntity.direct_counter_entry,
             p4runtime_pb2.DirectCounterEntry, direct_counter_name,
             modify_only=True)
@@ -5140,7 +5130,8 @@ class DirectCounterEntry(_CounterEntryBase):
         except KeyError as ex:
             raise InvalidP4InfoError(f"direct_table_id {self._direct_table_id} "
                                      f"is not a valid table id") from ex
-        self._table_entry = TableEntry(self._direct_table_name)
+        self._table_entry = TableEntry(p4_client, self._direct_table_name)
+        self.local_client = p4_client
         self.__doc__ = f"""
 An entry for direct counter '{direct_counter_name}'
 
@@ -5167,7 +5158,7 @@ To write to the counter, use <self>.modify
             raise UserError("Direct counters are not index-based")
         if name == "table_entry":
             if value is None:
-                self._table_entry = TableEntry(self._direct_table_name)
+                self._table_entry = TableEntry(self.local_client, self._direct_table_name)
                 return
             if not isinstance(value, TableEntry):
                 raise UserError("table_entry must be an instance of TableEntry")
@@ -5221,7 +5212,7 @@ class _MeterEntryBase(_P4EntityBase):
     Basic P4 meter entry.
     """
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, p4_client, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self._meter_type = self._info.spec.unit
         self.index = -1
@@ -5291,8 +5282,8 @@ class MeterEntry(_MeterEntryBase):
     P4 meter entry.
     """
 
-    def __init__(self, meter_name=None):
-        super().__init__(
+    def __init__(self, p4_client, meter_name=None):
+        super().__init__(p4_client,
             P4Type.meter, P4RuntimeEntity.meter_entry,
             p4runtime_pb2.MeterEntry, meter_name,
             modify_only=True)
@@ -5356,9 +5347,10 @@ class DirectMeterEntry(_MeterEntryBase):
     """
     Direct P4 meter entry.
     """
+    local_client = None
 
-    def __init__(self, direct_meter_name=None):
-        super().__init__(
+    def __init__(self, p4_client, direct_meter_name=None):
+        super().__init__(p4_client,
             P4Type.direct_meter, P4RuntimeEntity.direct_meter_entry,
             p4runtime_pb2.DirectMeterEntry, direct_meter_name,
             modify_only=True)
@@ -5369,7 +5361,8 @@ class DirectMeterEntry(_MeterEntryBase):
         except KeyError as ex:
             raise InvalidP4InfoError(f"direct_table_id {self._direct_table_id} "
                                      f"is not a valid table id") from ex
-        self._table_entry = TableEntry(self._direct_table_name)
+        self._table_entry = TableEntry(p4_client, self._direct_table_name)
+        self.local_client = p4_client
         self.__doc__ = f"""
 An entry for direct meter '{direct_meter_name}'
 
@@ -5399,7 +5392,7 @@ To write to the meter, use <self>.modify
             raise UserError("Direct meters are not index-based")
         if name == "table_entry":
             if value is None:
-                self._table_entry = TableEntry(self._direct_table_name)
+                self._table_entry = TableEntry(self.local_client, self._direct_table_name)
                 return
             if not isinstance(value, TableEntry):
                 raise UserError("table_entry must be an instance of TableEntry")
@@ -5531,8 +5524,8 @@ class MulticastGroupEntry(_EntityBase):
     P4 multicast group entry.
     """
 
-    def __init__(self, group_id=0):
-        super().__init__(
+    def __init__(self, p4_client, group_id=0):
+        super().__init__(p4_client,
             P4RuntimeEntity.packet_replication_engine_entry,
             p4runtime_pb2.PacketReplicationEngineEntry)
         self.group_id = group_id
@@ -5609,8 +5602,8 @@ class CloneSessionEntry(_EntityBase):
     P4 clone session entry.
     """
 
-    def __init__(self, session_id=0):
-        super().__init__(
+    def __init__(self, p4_client, session_id=0):
+        super().__init__(p4_client,
             P4RuntimeEntity.packet_replication_engine_entry,
             p4runtime_pb2.PacketReplicationEngineEntry)
         self.session_id = session_id
@@ -5779,8 +5772,9 @@ class PacketIn():
     """
     P4 packet in.
     """
+    local_client = None
 
-    def __init__(self):
+    def __init__(self, p4_client):
         ctrl_pkt_md = P4Objects(P4Type.controller_packet_metadata)
         self.md_info_list = {}
         if "packet_in" in ctrl_pkt_md:
@@ -5788,10 +5782,11 @@ class PacketIn():
             for md_info in self.p4_info.metadata:
                 self.md_info_list[md_info.name] = md_info
         self.packet_in_queue = queue.Queue()
+        self.local_client = p4_client
 
         def _packet_in_recv_func(packet_in_queue):
             while True:
-                msg = CLIENT.get_stream_packet("packet", timeout=None)
+                msg = self.local_client.get_stream_packet("packet", timeout=None)
                 if not msg:
                     break
                 packet_in_queue.put(msg)
@@ -5857,8 +5852,9 @@ class PacketOut:
     """
     P4 packet out.
     """
+    local_client = None
 
-    def __init__(self, payload=b'', **kwargs):
+    def __init__(self, p4_client, payload=b'', **kwargs):
 
         self.p4_info = P4Objects(P4Type.controller_packet_metadata)[
             "packet_out"]
@@ -5868,6 +5864,7 @@ class PacketOut:
         if kwargs:
             for key, value in kwargs.items():
                 self.metadata[key] = value
+        self.local_client = p4_client
 
     def _update_msg(self):
         self._entry = p4runtime_pb2.PacketOut()
@@ -5897,7 +5894,7 @@ class PacketOut:
         self._update_msg()
         msg = p4runtime_pb2.StreamMessageRequest()
         msg.packet.CopyFrom(self._entry)
-        CLIENT.stream_out_q.put(msg)
+        self.local_client.stream_out_q.put(msg)
 
     def str(self):
         """
@@ -5913,13 +5910,16 @@ class IdleTimeoutNotification():
     """
     P4 idle timeout notification.
     """
+    
+    local_client = None
 
-    def __init__(self):
+    def __init__(self, p4_client):
         self.notification_queue = queue.Queue()
+        self.local_client = p4_client.local_client
 
         def _notification_recv_func(notification_queue):
             while True:
-                msg = CLIENT.get_stream_packet("idle_timeout_notification",
+                msg = self.local_client.get_stream_packet("idle_timeout_notification",
                                                timeout=None)
                 if not msg:
                     break