Add support for P4Runtime clone sessions via Group API

Clone sessions can now be created by defining groups with new type CLONE

The PI framework has been refactored to abstract commonality between
multicast groups and clone sessions as both are managed as part of the
P4Runtime packet replication engine (PRE).

Change-Id: I2f23c629b7de1931d5cab96ec76aef26130ce418
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
index 84678dc..f38b504 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
@@ -21,7 +21,6 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupDescription;
 import org.onosproject.net.group.GroupOperation;
 import org.onosproject.net.group.GroupOperations;
 import org.onosproject.net.group.GroupProgrammable;
@@ -37,7 +36,7 @@
 /**
  * Implementation of GroupProgrammable for P4Runtime devices that uses two
  * different implementation of the same behavior to handle both action profile
- * groups and multicast groups.
+ * groups and PRE entries.
  */
 public class P4RuntimeGroupProgrammable
         extends AbstractHandlerBehaviour implements GroupProgrammable {
@@ -49,28 +48,36 @@
         checkArgument(deviceId.equals(data().deviceId()),
                       "passed deviceId must be the same assigned to this behavior");
         final List<GroupOperation> actionGroups = Lists.newArrayList();
-        final List<GroupOperation> multicastGroups = Lists.newArrayList();
+        final List<GroupOperation> preGroups = Lists.newArrayList();
         groupOps.operations().forEach(op -> {
-            if (op.groupType().equals(GroupDescription.Type.ALL)) {
-                multicastGroups.add(op);
-            } else {
-                actionGroups.add(op);
+            switch (op.groupType()) {
+                case SELECT:
+                    actionGroups.add(op);
+                    break;
+                case ALL:
+                case CLONE:
+                    preGroups.add(op);
+                    break;
+                case FAILOVER:
+                case INDIRECT:
+                default:
+                    log.warn("{} group type not supported [{}]", op.groupType(), op);
             }
         });
         if (!actionGroups.isEmpty()) {
             actionProgrammable().performGroupOperation(
                     deviceId, new GroupOperations(actionGroups));
         }
-        if (!multicastGroups.isEmpty()) {
-            multicastProgrammable().performGroupOperation(
-                    deviceId, new GroupOperations(multicastGroups));
+        if (!preGroups.isEmpty()) {
+            replicationProgrammable().performGroupOperation(
+                    deviceId, new GroupOperations(preGroups));
         }
     }
 
     private Collection<Group> doGetGroups() {
         return new ImmutableList.Builder<Group>()
                 .addAll(actionProgrammable().getGroups())
-                .addAll(multicastProgrammable().getGroups())
+                .addAll(replicationProgrammable().getGroups())
                 .build();
     }
 
@@ -81,8 +88,8 @@
         return prog;
     }
 
-    private P4RuntimeMulticastGroupProgrammable multicastProgrammable() {
-        P4RuntimeMulticastGroupProgrammable prog = new P4RuntimeMulticastGroupProgrammable();
+    private P4RuntimeReplicationGroupProgrammable replicationProgrammable() {
+        P4RuntimeReplicationGroupProgrammable prog = new P4RuntimeReplicationGroupProgrammable();
         prog.setData(data());
         prog.setHandler(handler());
         return prog;
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMulticastGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMulticastGroupProgrammable.java
deleted file mode 100644
index 212ada2..0000000
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMulticastGroupProgrammable.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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.
- */
-
-package org.onosproject.drivers.p4runtime;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Striped;
-import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMulticastGroupMirror;
-import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.group.DefaultGroup;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupOperation;
-import org.onosproject.net.group.GroupOperations;
-import org.onosproject.net.group.GroupProgrammable;
-import org.onosproject.net.group.GroupStore;
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
-import org.onosproject.net.pi.service.PiMulticastGroupTranslator;
-import org.onosproject.net.pi.service.PiTranslatedEntity;
-import org.onosproject.net.pi.service.PiTranslationException;
-import org.onosproject.p4runtime.api.P4RuntimeClient;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.locks.Lock;
-import java.util.stream.Collectors;
-
-import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.DELETE;
-import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.INSERT;
-import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.MODIFY;
-
-/**
- * Implementation of GroupProgrammable to handle multicast groups in P4Runtime.
- */
-public class P4RuntimeMulticastGroupProgrammable
-        extends AbstractP4RuntimeHandlerBehaviour implements GroupProgrammable {
-
-    // TODO: implement reading groups from device and mirror sync.
-
-    // Needed to synchronize operations over the same group.
-    private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
-
-    private GroupStore groupStore;
-    private P4RuntimeMulticastGroupMirror mcGroupMirror;
-    private PiMulticastGroupTranslator mcGroupTranslator;
-
-    @Override
-    protected boolean setupBehaviour(String opName) {
-        if (!super.setupBehaviour(opName)) {
-            return false;
-        }
-        mcGroupMirror = this.handler().get(P4RuntimeMulticastGroupMirror.class);
-        groupStore = handler().get(GroupStore.class);
-        mcGroupTranslator = translationService.multicastGroupTranslator();
-        return true;
-    }
-
-    @Override
-    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
-        if (!setupBehaviour("performGroupOperation()")) {
-            return;
-        }
-        groupOps.operations().stream()
-                .filter(op -> op.groupType().equals(GroupDescription.Type.ALL))
-                .forEach(op -> {
-                    final Group group = groupStore.getGroup(deviceId, op.groupId());
-                    if (group == null) {
-                        log.warn("Unable to find group {} in store, aborting {} operation [{}]",
-                                 op.groupId(), op.opType(), op);
-                        return;
-                    }
-                    processMcGroupOp(group, op.opType());
-                });
-    }
-
-    @Override
-    public Collection<Group> getGroups() {
-        if (!setupBehaviour("getGroups()")) {
-            return Collections.emptyList();
-        }
-        return ImmutableList.copyOf(getMcGroups());
-    }
-
-    private Collection<Group> getMcGroups() {
-        // TODO: missing support for reading multicast groups in PI/Stratum.
-        return getMcGroupsFromMirror();
-    }
-
-    private Collection<Group> getMcGroupsFromMirror() {
-        return mcGroupMirror.getAll(deviceId).stream()
-                .map(TimedEntry::entry)
-                .map(this::forgeMcGroupEntry)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toList());
-    }
-
-    private void processMcGroupOp(Group pdGroup, GroupOperation.Type opType) {
-        final PiMulticastGroupEntry mcGroup;
-        try {
-            mcGroup = mcGroupTranslator.translate(pdGroup, pipeconf);
-        } catch (PiTranslationException e) {
-            log.warn("Unable to translate multicast group, aborting {} operation: {} [{}]",
-                     opType, e.getMessage(), pdGroup);
-            return;
-        }
-        final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
-                deviceId, mcGroup);
-        final PiMulticastGroupEntry groupOnDevice = mcGroupMirror.get(handle) == null
-                ? null
-                : mcGroupMirror.get(handle).entry();
-        final Lock lock = STRIPED_LOCKS.get(handle);
-        lock.lock();
-        try {
-            processMcGroup(handle, mcGroup,
-                           groupOnDevice, pdGroup, opType);
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    private void processMcGroup(PiMulticastGroupEntryHandle handle,
-                                PiMulticastGroupEntry groupToApply,
-                                PiMulticastGroupEntry groupOnDevice,
-                                Group pdGroup, GroupOperation.Type opType) {
-        switch (opType) {
-            case ADD:
-                robustMcGroupAdd(handle, groupToApply, pdGroup);
-                return;
-            case MODIFY:
-                // Since reading multicast groups is not supported yet on
-                // PI/Stratum, we cannot trust groupOnDevice as we don't have a
-                // mechanism to enforce consistency of the mirror with the
-                // device state.
-                // if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE,
-                //                        DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
-                //         && p4OpType == MODIFY
-                //         && groupOnDevice != null
-                //         && groupOnDevice.equals(groupToApply)) {
-                //     // Ignore.
-                //     return;
-                // }
-                robustMcGroupModify(handle, groupToApply, pdGroup);
-                return;
-            case DELETE:
-                mcGroupApply(handle, groupToApply, pdGroup, DELETE);
-                return;
-            default:
-                log.error("Unknown group operation type {}, " +
-                                  "cannot process multicast group", opType);
-        }
-    }
-
-    private boolean writeMcGroupOnDevice(
-            PiMulticastGroupEntry group, P4RuntimeClient.UpdateType opType) {
-        return client.write(p4DeviceId, pipeconf)
-                .entity(group, opType).submitSync().isSuccess();
-    }
-
-    private boolean mcGroupApply(PiMulticastGroupEntryHandle handle,
-                                 PiMulticastGroupEntry piGroup,
-                                 Group pdGroup,
-                                 P4RuntimeClient.UpdateType opType) {
-        switch (opType) {
-            case DELETE:
-                if (writeMcGroupOnDevice(piGroup, DELETE)) {
-                    mcGroupMirror.remove(handle);
-                    mcGroupTranslator.forget(handle);
-                    return true;
-                } else {
-                    return false;
-                }
-            case INSERT:
-            case MODIFY:
-                if (writeMcGroupOnDevice(piGroup, opType)) {
-                    mcGroupMirror.put(handle, piGroup);
-                    mcGroupTranslator.learn(handle, new PiTranslatedEntity<>(
-                            pdGroup, piGroup, handle));
-                    return true;
-                } else {
-                    return false;
-                }
-            default:
-                log.warn("Unknown operation type {}, cannot apply group", opType);
-                return false;
-        }
-    }
-
-    private void robustMcGroupAdd(PiMulticastGroupEntryHandle handle,
-                                  PiMulticastGroupEntry piGroup,
-                                  Group pdGroup) {
-        if (mcGroupApply(handle, piGroup, pdGroup, INSERT)) {
-            return;
-        }
-        // Try to delete (perhaps it already exists) and re-add...
-        mcGroupApply(handle, piGroup, pdGroup, DELETE);
-        mcGroupApply(handle, piGroup, pdGroup, INSERT);
-    }
-
-    private void robustMcGroupModify(PiMulticastGroupEntryHandle handle,
-                                     PiMulticastGroupEntry piGroup,
-                                     Group pdGroup) {
-        if (mcGroupApply(handle, piGroup, pdGroup, MODIFY)) {
-            return;
-        }
-        // Not sure for which reason it cannot be modified, so try to delete and insert instead...
-        mcGroupApply(handle, piGroup, pdGroup, DELETE);
-        mcGroupApply(handle, piGroup, pdGroup, INSERT);
-    }
-
-    private Group forgeMcGroupEntry(PiMulticastGroupEntry mcGroup) {
-        final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
-                deviceId, mcGroup);
-        final Optional<PiTranslatedEntity<Group, PiMulticastGroupEntry>>
-                translatedEntity = mcGroupTranslator.lookup(handle);
-        final TimedEntry<PiMulticastGroupEntry> timedEntry = mcGroupMirror.get(handle);
-        // Is entry consistent with our state?
-        if (!translatedEntity.isPresent()) {
-            log.warn("Multicast group handle not found in translation store: {}", handle);
-            return null;
-        }
-        if (timedEntry == null) {
-            log.warn("Multicast group handle not found in device mirror: {}", handle);
-            return null;
-        }
-        return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
-    }
-
-    private Group addedGroup(Group original, long life) {
-        final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
-        forgedGroup.setState(Group.GroupState.ADDED);
-        forgedGroup.setLife(life);
-        return forgedGroup;
-    }
-
-}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeReplicationGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeReplicationGroupProgrammable.java
new file mode 100644
index 0000000..483bd0e
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeReplicationGroupProgrammable.java
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+package org.onosproject.drivers.p4runtime;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Striped;
+import org.onosproject.drivers.p4runtime.mirror.P4RuntimePreEntryMirror;
+import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProgrammable;
+import org.onosproject.net.group.GroupStore;
+import org.onosproject.net.pi.runtime.PiPreEntry;
+import org.onosproject.net.pi.runtime.PiPreEntryHandle;
+import org.onosproject.net.pi.service.PiReplicationGroupTranslator;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationException;
+import org.onosproject.p4runtime.api.P4RuntimeClient;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.locks.Lock;
+import java.util.stream.Collectors;
+
+import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.DELETE;
+import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.INSERT;
+import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.MODIFY;
+
+/**
+ * Implementation of GroupProgrammable to handle PRE entries in P4Runtime.
+ */
+public class P4RuntimeReplicationGroupProgrammable
+        extends AbstractP4RuntimeHandlerBehaviour implements GroupProgrammable {
+
+    // TODO: implement reading groups from device and mirror sync.
+
+    // Needed to synchronize operations over the same group.
+    private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
+
+    private GroupStore groupStore;
+    private P4RuntimePreEntryMirror mirror;
+    private PiReplicationGroupTranslator translator;
+
+    @Override
+    protected boolean setupBehaviour(String opName) {
+        if (!super.setupBehaviour(opName)) {
+            return false;
+        }
+        mirror = this.handler().get(P4RuntimePreEntryMirror.class);
+        groupStore = handler().get(GroupStore.class);
+        translator = translationService.replicationGroupTranslator();
+        return true;
+    }
+
+    @Override
+    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
+        if (!setupBehaviour("performGroupOperation()")) {
+            return;
+        }
+        groupOps.operations().stream()
+                .filter(op -> op.groupType().equals(GroupDescription.Type.ALL))
+                .forEach(op -> {
+                    final Group group = groupStore.getGroup(deviceId, op.groupId());
+                    if (group == null) {
+                        log.warn("Unable to find group {} in store, aborting {} operation [{}]",
+                                 op.groupId(), op.opType(), op);
+                        return;
+                    }
+                    processGroupOp(group, op.opType());
+                });
+    }
+
+    @Override
+    public Collection<Group> getGroups() {
+        if (!setupBehaviour("getGroups()")) {
+            return Collections.emptyList();
+        }
+        // TODO: missing support for reading multicast groups in PI/Stratum.
+        return ImmutableList.copyOf(getGroupsFromMirror());
+    }
+
+    private Collection<Group> getGroupsFromMirror() {
+        return mirror.getAll(deviceId).stream()
+                .map(TimedEntry::entry)
+                .map(this::forgeGroupEntry)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    private void processGroupOp(Group pdGroup, GroupOperation.Type opType) {
+        final PiPreEntry preEntry;
+        try {
+            preEntry = translator.translate(pdGroup, pipeconf);
+        } catch (PiTranslationException e) {
+            log.warn("Unable to translate replication group, aborting {} operation: {} [{}]",
+                     opType, e.getMessage(), pdGroup);
+            return;
+        }
+        final PiPreEntryHandle handle = (PiPreEntryHandle) preEntry.handle(deviceId);
+        final PiPreEntry entryOnDevice = mirror.get(handle) == null
+                ? null : mirror.get(handle).entry();
+        final Lock lock = STRIPED_LOCKS.get(handle);
+        lock.lock();
+        try {
+            processPreEntry(handle, preEntry,
+                            entryOnDevice, pdGroup, opType);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void processPreEntry(PiPreEntryHandle handle,
+                                 PiPreEntry entryToApply,
+                                 PiPreEntry entryOnDevice,
+                                 Group pdGroup, GroupOperation.Type opType) {
+        switch (opType) {
+            case ADD:
+                robustInsert(handle, entryToApply, pdGroup);
+                return;
+            case MODIFY:
+                // Since reading multicast groups is not supported yet on
+                // PI/Stratum, we cannot trust groupOnDevice as we don't have a
+                // mechanism to enforce consistency of the mirror with the
+                // device state.
+                // if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE,
+                //                        DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
+                //         && p4OpType == MODIFY
+                //         && groupOnDevice != null
+                //         && groupOnDevice.equals(groupToApply)) {
+                //     // Ignore.
+                //     return;
+                // }
+                robustModify(handle, entryToApply, pdGroup);
+                return;
+            case DELETE:
+                preEntryWrite(handle, entryToApply, pdGroup, DELETE);
+                return;
+            default:
+                log.error("Unknown group operation type {}, " +
+                                  "cannot process multicast group", opType);
+        }
+    }
+
+    private boolean writeEntryOnDevice(
+            PiPreEntry entry, P4RuntimeClient.UpdateType opType) {
+        return client.write(p4DeviceId, pipeconf)
+                .entity(entry, opType).submitSync().isSuccess();
+    }
+
+    private boolean preEntryWrite(PiPreEntryHandle handle,
+                                  PiPreEntry preEntry,
+                                  Group pdGroup,
+                                  P4RuntimeClient.UpdateType opType) {
+        switch (opType) {
+            case DELETE:
+                if (writeEntryOnDevice(preEntry, DELETE)) {
+                    mirror.remove(handle);
+                    translator.forget(handle);
+                    return true;
+                } else {
+                    return false;
+                }
+            case INSERT:
+            case MODIFY:
+                if (writeEntryOnDevice(preEntry, opType)) {
+                    mirror.put(handle, preEntry);
+                    translator.learn(handle, new PiTranslatedEntity<>(
+                            pdGroup, preEntry, handle));
+                    return true;
+                } else {
+                    return false;
+                }
+            default:
+                log.warn("Unknown operation type {}, cannot apply group", opType);
+                return false;
+        }
+    }
+
+    private void robustInsert(PiPreEntryHandle handle,
+                              PiPreEntry preEntry,
+                              Group pdGroup) {
+        if (preEntryWrite(handle, preEntry, pdGroup, INSERT)) {
+            return;
+        }
+        // Try to delete (perhaps it already exists) and re-add...
+        preEntryWrite(handle, preEntry, pdGroup, DELETE);
+        preEntryWrite(handle, preEntry, pdGroup, INSERT);
+    }
+
+    private void robustModify(PiPreEntryHandle handle,
+                              PiPreEntry preEntry,
+                              Group pdGroup) {
+        if (preEntryWrite(handle, preEntry, pdGroup, MODIFY)) {
+            return;
+        }
+        // Not sure for which reason it cannot be modified, so try to delete and insert instead...
+        preEntryWrite(handle, preEntry, pdGroup, DELETE);
+        preEntryWrite(handle, preEntry, pdGroup, INSERT);
+    }
+
+    private Group forgeGroupEntry(PiPreEntry preEntry) {
+        final PiPreEntryHandle handle = (PiPreEntryHandle) preEntry.handle(deviceId);
+        final Optional<PiTranslatedEntity<Group, PiPreEntry>>
+                translatedEntity = translator.lookup(handle);
+        final TimedEntry<PiPreEntry> timedEntry = mirror.get(handle);
+        // Is entry consistent with our state?
+        if (!translatedEntity.isPresent()) {
+            log.warn("PRE entry handle not found in translation store: {}", handle);
+            return null;
+        }
+        if (timedEntry == null) {
+            log.warn("PRE entry handle not found in device mirror: {}", handle);
+            return null;
+        }
+        return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
+    }
+
+    private Group addedGroup(Group original, long life) {
+        final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
+        forgedGroup.setState(Group.GroupState.ADDED);
+        forgedGroup.setLife(life);
+        return forgedGroup;
+    }
+
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMulticastGroupMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMulticastGroupMirror.java
deleted file mode 100644
index 5ccaec5..0000000
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMulticastGroupMirror.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-package org.onosproject.drivers.p4runtime.mirror;
-
-import org.onosproject.net.pi.runtime.PiEntityType;
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
-import org.osgi.service.component.annotations.Component;
-
-/**
- * Distributed implementation of a P4Runtime multicast group mirror.
- */
-@Component(immediate = true, service = P4RuntimeMulticastGroupMirror.class)
-public final class DistributedP4RuntimeMulticastGroupMirror
-        extends AbstractDistributedP4RuntimeMirror
-                        <PiMulticastGroupEntryHandle, PiMulticastGroupEntry>
-        implements P4RuntimeMulticastGroupMirror {
-
-    public DistributedP4RuntimeMulticastGroupMirror() {
-        super(PiEntityType.PRE_MULTICAST_GROUP_ENTRY);
-    }
-}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimePreEntryMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimePreEntryMirror.java
new file mode 100644
index 0000000..4876aaa
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimePreEntryMirror.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.onosproject.net.pi.runtime.PiEntityType;
+import org.onosproject.net.pi.runtime.PiPreEntry;
+import org.onosproject.net.pi.runtime.PiPreEntryHandle;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Distributed implementation of a P4Runtime PRE entry mirror.
+ */
+@Component(immediate = true, service = P4RuntimePreEntryMirror.class)
+public final class DistributedP4RuntimePreEntryMirror
+        extends AbstractDistributedP4RuntimeMirror<PiPreEntryHandle, PiPreEntry>
+        implements P4RuntimePreEntryMirror {
+
+    public DistributedP4RuntimePreEntryMirror() {
+        super(PiEntityType.PRE_ENTRY);
+    }
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMulticastGroupMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimePreEntryMirror.java
similarity index 67%
rename from drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMulticastGroupMirror.java
rename to drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimePreEntryMirror.java
index d901f29..c6a5fcf 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMulticastGroupMirror.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimePreEntryMirror.java
@@ -16,12 +16,12 @@
 
 package org.onosproject.drivers.p4runtime.mirror;
 
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
-import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
+import org.onosproject.net.pi.runtime.PiPreEntry;
+import org.onosproject.net.pi.runtime.PiPreEntryHandle;
 
 /**
- * Mirror of multicast groups installed on a P4Runtime device.
+ * Mirror of PRE entries installed on a P4Runtime device.
  */
-public interface P4RuntimeMulticastGroupMirror
-        extends P4RuntimeMirror<PiMulticastGroupEntryHandle, PiMulticastGroupEntry> {
+public interface P4RuntimePreEntryMirror
+        extends P4RuntimeMirror<PiPreEntryHandle, PiPreEntry> {
 }