ONOS-7739 Support for P4Runtime multicast programming
Design doc: https://docs.google.com/document/d/13rkQlwr49M-uxQQEuxCMP7BFEPY2gtQ850Hn3gUfesU/edit#heading=h.lzdayib259sq
Change-Id: Ief00bec89fe5a9784b0ee13fdaafa3ae58ab654f
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
index a6c612b..0569f99 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
@@ -46,5 +46,15 @@
/**
* Register entry.
*/
- REGISTER_CELL
+ REGISTER_CELL,
+
+ /**
+ * Packet Replication Engine (PRE) multicast group entry.
+ */
+ PRE_MULTICAST_GROUP_ENTRY,
+
+ /**
+ * Packet Replication Engine (PRE) clone session entry.
+ */
+ PRE_CLONE_SESSION_ENTRY
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntry.java
new file mode 100644
index 0000000..5f68e40
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntry.java
@@ -0,0 +1,168 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Collection;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of multicast group entry of a protocol-independent packet
+ * replication engine (PRE).
+ */
+@Beta
+public final class PiMulticastGroupEntry implements PiPreEntry {
+
+ private final long groupId;
+ private final Set<PiPreReplica> replicas;
+
+ private PiMulticastGroupEntry(long groupId, Set<PiPreReplica> replicas) {
+ this.groupId = groupId;
+ this.replicas = replicas;
+ }
+
+ /**
+ * Returns the identifier of this multicast group, unique in the scope of a
+ * PRE instance.
+ *
+ * @return group entry ID
+ */
+ public long groupId() {
+ return groupId;
+ }
+
+ /**
+ * Returns the packet replicas provided by this multicast group.
+ *
+ * @return packet replicas
+ */
+ public Set<PiPreReplica> replicas() {
+ return replicas;
+ }
+
+ @Override
+ public PiPreEntryType preEntryType() {
+ return PiPreEntryType.MULTICAST_GROUP;
+ }
+
+ @Override
+ public PiEntityType piEntityType() {
+ return PiEntityType.PRE_MULTICAST_GROUP_ENTRY;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(groupId, replicas);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final PiMulticastGroupEntry other = (PiMulticastGroupEntry) obj;
+ return Objects.equal(this.groupId, other.groupId)
+ && Objects.equal(this.replicas, other.replicas);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("groupId", groupId)
+ .add("replicas", replicas)
+ .toString();
+ }
+
+ /**
+ * Returns a new builder of multicast group entries.
+ *
+ * @return builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of PI multicast group entries.
+ */
+ public static final class Builder {
+
+ private Long groupId;
+ private ImmutableSet.Builder<PiPreReplica> replicaSetBuilder = ImmutableSet.builder();
+
+ private Builder() {
+ // Hide constructor.
+ }
+
+ /**
+ * Sets the identifier of this multicast group.
+ *
+ * @param groupId group ID
+ * @return this
+ */
+ public Builder withGroupId(long groupId) {
+ this.groupId = groupId;
+ return this;
+ }
+
+ /**
+ * Adds the given packet replica to this multicast group.
+ *
+ * @param replica packet replica
+ * @return this
+ */
+ public Builder addReplica(PiPreReplica replica) {
+ checkNotNull(replica);
+ replicaSetBuilder.add(replica);
+ return this;
+ }
+
+ /**
+ * Adds the given packet replicas to this multicast group.
+ *
+ * @param replicas packet replicas
+ * @return this
+ */
+ public Builder addReplicas(Collection<PiPreReplica> replicas) {
+ checkNotNull(replicas);
+ replicaSetBuilder.addAll(replicas);
+ return this;
+ }
+
+ /**
+ * Returns a new multicast group entry.
+ *
+ * @return multicast group entry
+ */
+ public PiMulticastGroupEntry build() {
+ checkNotNull(groupId, "Multicast group ID must be set");
+ final ImmutableSet<PiPreReplica> replicas = replicaSetBuilder.build();
+ checkArgument(!replicas.isEmpty(), "At least one replica must be defined");
+ return new PiMulticastGroupEntry(groupId, replicas);
+ }
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryHandle.java
new file mode 100644
index 0000000..f9b1170
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryHandle.java
@@ -0,0 +1,74 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Global identifier of a PI multicast group entry applied to the packet
+ * replication engine of a device, uniquely defined by a device ID, and group
+ * ID.
+ */
+@Beta
+public final class PiMulticastGroupEntryHandle extends PiHandle<PiMulticastGroupEntry> {
+
+ private PiMulticastGroupEntryHandle(DeviceId deviceId, PiMulticastGroupEntry entry) {
+ super(deviceId, entry);
+ }
+
+ /**
+ * Creates a new handle for the given device ID and PI multicast group
+ * entry.
+ *
+ * @param deviceId device ID
+ * @param entry PI multicast group entry
+ * @return PI multicast group entry handle
+ */
+ public static PiMulticastGroupEntryHandle of(DeviceId deviceId,
+ PiMulticastGroupEntry entry) {
+ return new PiMulticastGroupEntryHandle(deviceId, entry);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(deviceId(), piEntity().groupId());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PiMulticastGroupEntryHandle that = (PiMulticastGroupEntryHandle) o;
+ return Objects.equal(deviceId(), that.deviceId()) &&
+ Objects.equal(piEntity().groupId(), that.piEntity().groupId());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("deviceId", deviceId())
+ .add("groupId", piEntity().groupId())
+ .toString();
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreEntry.java
new file mode 100644
index 0000000..fb6c5e0
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreEntry.java
@@ -0,0 +1,48 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Configuration entry of a Packet Replication Engine (PRE) of
+ * protocol-independent pipeline.
+ */
+@Beta
+public interface PiPreEntry extends PiEntity {
+
+ /**
+ * Type of PRE entry.
+ */
+ enum PiPreEntryType {
+ /**
+ * Multicast group entry.
+ */
+ MULTICAST_GROUP,
+ /**
+ * Clone session entry.
+ */
+ CLONE_SESSION
+ }
+
+ /**
+ * Returns the type of this PRE entry.
+ *
+ * @return PRE entry type
+ */
+ PiPreEntryType preEntryType();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreReplica.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreReplica.java
new file mode 100644
index 0000000..ea7003d
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPreReplica.java
@@ -0,0 +1,89 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.PortNumber;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+
+/**
+ * Representation of a packet replica used for multicast or cloning process in a
+ * protocol-independent packet replication engine.
+ * <p>
+ * Each replica is uniquely identified inside a given multicast group or clone
+ * session by the pair (egress port, instance ID).
+ */
+public class PiPreReplica {
+
+ private final PortNumber egressPort;
+ private final long instanceId;
+
+ /**
+ * Returns a new PRE packet replica for the given egress port and instance
+ * ID.
+ *
+ * @param egressPort egress port
+ * @param instanceId instance ID
+ */
+ public PiPreReplica(PortNumber egressPort, long instanceId) {
+ this.egressPort = checkNotNull(egressPort);
+ this.instanceId = instanceId;
+ }
+
+ /**
+ * Returns the egress port of this replica.
+ *
+ * @return egress port
+ */
+ public PortNumber egressPort() {
+ return egressPort;
+ }
+
+ /**
+ * Returns the instance ID of this replica.
+ *
+ * @return instance ID
+ */
+ public long instanceId() {
+ return instanceId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(egressPort, instanceId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final PiPreReplica other = (PiPreReplica) obj;
+ return Objects.equal(this.egressPort, other.egressPort)
+ && Objects.equal(this.instanceId, other.instanceId);
+ }
+
+ @Override
+ public String toString() {
+ return format("%s:%d", egressPort, instanceId);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslationStore.java
new file mode 100644
index 0000000..f961d84
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslationStore.java
@@ -0,0 +1,30 @@
+/*
+ * 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.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
+
+/**
+ * A PI translation store that keeps track of which groups have been
+ * translated to which PI PRE multicast groups.
+ */
+@Beta
+public interface PiMulticastGroupTranslationStore
+ extends PiTranslationStore<Group, PiMulticastGroupEntry> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslator.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslator.java
new file mode 100644
index 0000000..e687189
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiMulticastGroupTranslator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
+
+/**
+ * A translator of groups to PI multicast group.
+ */
+@Beta
+public interface PiMulticastGroupTranslator
+ extends PiTranslator<Group, PiMulticastGroupEntry> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
index 20cef35..20ea14d 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
@@ -45,4 +45,12 @@
* @return meter translator
*/
PiMeterTranslator meterTranslator();
+
+ /**
+ * Returns a group translator for packet replication engine (PRE)
+ * multicast groups.
+ *
+ * @return multicast group translator
+ */
+ PiMulticastGroupTranslator multicastGroupTranslator();
}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryTest.java
new file mode 100644
index 0000000..7c060ef
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMulticastGroupEntryTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.PortNumber;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Tests for {@link PiMulticastGroupEntry}.
+ */
+public class PiMulticastGroupEntryTest {
+ private final long groupId1 = 1;
+ private final long groupId2 = 2;
+
+ private final long instanceId1 = 1;
+
+ private final PortNumber port1 = PortNumber.portNumber(1);
+ private final PortNumber port2 = PortNumber.portNumber(2);
+ private final PortNumber port3 = PortNumber.portNumber(3);
+
+ private final PiPreReplica replica1 = new PiPreReplica(port1, instanceId1);
+ private final PiPreReplica replica2 = new PiPreReplica(port2, instanceId1);
+ private final PiPreReplica replica3 = new PiPreReplica(port3, instanceId1);
+
+ private final PiMulticastGroupEntry group1 = PiMulticastGroupEntry.builder()
+ .withGroupId(groupId1)
+ .addReplica(replica1)
+ .addReplica(replica2)
+ .build();
+
+ private final PiMulticastGroupEntry sameAsGroup1 = PiMulticastGroupEntry.builder()
+ .withGroupId(groupId1)
+ .addReplica(replica1)
+ .addReplica(replica2)
+ .build();
+
+ private final PiMulticastGroupEntry group2 = PiMulticastGroupEntry.builder()
+ .withGroupId(groupId2)
+ .addReplica(replica1)
+ .addReplica(replica2)
+ .addReplica(replica3)
+ .build();
+
+ @Test
+ public void testPiMulticastGroupEntry() {
+ assertThat("Invalid group ID",
+ group1.groupId(), is(groupId1));
+ assertThat("Invalid replicas size",
+ group1.replicas().size(), is(2));
+ assertThat("Invalid replicas",
+ group1.replicas(), contains(replica1, replica2));
+
+ assertThat("Invalid group ID",
+ group2.groupId(), is(groupId2));
+ assertThat("Invalid replicas size",
+ group2.replicas().size(), is(3));
+ assertThat("Invalid replicas",
+ group2.replicas(), contains(replica1, replica2, replica3));
+ }
+
+ @Test
+ public void testEquality() {
+ new EqualsTester()
+ .addEqualityGroup(group1, sameAsGroup1)
+ .addEqualityGroup(group2)
+ .testEquals();
+ }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiPreReplicaTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiPreReplicaTest.java
new file mode 100644
index 0000000..85ea36e
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiPreReplicaTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.PortNumber;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Tests for {@link PiPreReplica}.
+ */
+public class PiPreReplicaTest {
+
+ private final long instanceId1 = 1;
+ private final long instanceId2 = 2;
+ private final PortNumber port1 = PortNumber.portNumber(1);
+ private final PortNumber port2 = PortNumber.portNumber(2);
+
+ private final PiPreReplica replica1of1 = new PiPreReplica(port1, instanceId1);
+ private final PiPreReplica sameAsReplica1of1 = new PiPreReplica(port1, instanceId1);
+
+ private final PiPreReplica replica1of2 = new PiPreReplica(port2, instanceId1);
+ private final PiPreReplica sameAsReplica1of2 = new PiPreReplica(port2, instanceId1);
+
+ private final PiPreReplica replica2of2 = new PiPreReplica(port2, instanceId2);
+ private final PiPreReplica sameAsReplica2of2 = new PiPreReplica(port2, instanceId2);
+
+ @Test
+ public void testPiPreReplica() {
+ assertThat("Invalid port", replica1of1.egressPort(), is(port1));
+ assertThat("Invalid instance ID", replica1of1.instanceId(), is(instanceId1));
+ assertThat("Invalid port", replica1of2.egressPort(), is(port2));
+ assertThat("Invalid instance ID", replica1of2.instanceId(), is(instanceId1));
+ }
+
+ @Test
+ public void testEquality() {
+ new EqualsTester()
+ .addEqualityGroup(replica1of1, sameAsReplica1of1)
+ .addEqualityGroup(replica1of2, sameAsReplica1of2)
+ .addEqualityGroup(replica2of2, sameAsReplica2of2)
+ .testEquals();
+ }
+}