ONOS-7050 First stab at PI translation store
Change-Id: I7f48802b1f5d70fbe3e6cead2800855de18b9207
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
index 27a604c..b62007f 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
@@ -18,12 +18,13 @@
import org.onosproject.core.ApplicationId;
import org.onosproject.core.GroupId;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.service.PiTranslatable;
/**
* Represents a generalized match & action pair to be applied to an
* infrastructure device.
*/
-public interface FlowRule {
+public interface FlowRule extends PiTranslatable {
IndexTableId DEFAULT_TABLE = IndexTableId.of(0);
int MAX_TIMEOUT = 60;
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
index 1d9a94b..9084bea 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
@@ -34,7 +34,7 @@
* Instance of an action group of a protocol-independent pipeline.
*/
@Beta
-public final class PiActionGroup {
+public final class PiActionGroup implements PiEntity {
private final PiActionGroupId id;
private final PiActionGroupType type;
@@ -125,6 +125,11 @@
return new Builder();
}
+ @Override
+ public PiEntityType piEntityType() {
+ return PiEntityType.GROUP;
+ }
+
/**
* Builder of action groups.
*/
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java
new file mode 100644
index 0000000..c3d5a01
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of an entity of a protocol-independent that can be read or write
+ * at runtime.
+ */
+@Beta
+public interface PiEntity {
+
+ /**
+ * Returns the type of this entity.
+ *
+ * @return entity type
+ */
+ PiEntityType piEntityType();
+}
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
new file mode 100644
index 0000000..e01e520
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Type of runtime entity of a protocol-independent pipeline.
+ */
+@Beta
+public enum PiEntityType {
+ /**
+ * Table entry.
+ */
+ TABLE_ENTRY,
+
+ /**
+ * Action profile group.
+ */
+ GROUP
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index cd7e493..5770c6b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -30,7 +30,7 @@
* Instance of a table entry in a protocol-independent pipeline.
*/
@Beta
-public final class PiTableEntry {
+public final class PiTableEntry implements PiEntity {
public static final PiTableEntry EMTPY = new PiTableEntry();
@@ -160,6 +160,11 @@
return new Builder();
}
+ @Override
+ public PiEntityType piEntityType() {
+ return PiEntityType.TABLE_ENTRY;
+ }
+
public static final class Builder {
private PiTableId tableId;
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java
new file mode 100644
index 0000000..316c3ed
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of protocol-dependent (PD) entity that can be translated to an
+ * equivalent protocol-independent (PI) one.
+ */
+@Beta
+public interface PiTranslatable {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java
new file mode 100644
index 0000000..8914eee
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiEntityType;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of the result of a PD-to-PI translation.
+ */
+@Beta
+public final class PiTranslatedEntity {
+
+ private final PiTranslatable original;
+ private final PiEntity translated;
+ private final PiPipeconfId pipeconfId;
+ private final PiEntityType type;
+
+ public PiTranslatedEntity(PiTranslatable original, PiEntity translated,
+ PiPipeconfId pipeconfId) {
+ this.original = checkNotNull(original);
+ this.translated = checkNotNull(translated);
+ this.pipeconfId = checkNotNull(pipeconfId);
+ this.type = checkNotNull(translated.piEntityType());
+ }
+
+ /**
+ * Returns the type of the translated entity.
+ *
+ * @return type of the translated entity
+ */
+ public final PiEntityType entityType() {
+ return type;
+ }
+
+ /**
+ * Returns the original PD entity.
+ *
+ * @return instance of PI translatable entity
+ */
+ public final PiTranslatable original() {
+ return original;
+ }
+
+ /**
+ * Returns the translated PI entity.
+ *
+ * @return PI entity
+ */
+ public final PiEntity translated() {
+ return translated;
+ }
+
+ /**
+ * The ID of the pipeconf for which this translation is valid. In other
+ * words, the PI entity is guaranteed to be functionally equivalent to the
+ * PD one when applied to a device configured with such pipeconf.
+ *
+ * @return PI pipeconf ID
+ */
+ public final PiPipeconfId pipeconfId() {
+ return pipeconfId;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java
new file mode 100644
index 0000000..4f59079
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Signals an event related to the translation of a protocol-dependent (PD)
+ * entity to a protocol-independent (PI) one.
+ */
+@Beta
+public final class PiTranslationEvent
+ extends AbstractEvent<PiTranslationEvent.Type, PiTranslatedEntity> {
+
+ /**
+ * Type of event.
+ */
+ public enum Type {
+ /**
+ * Signals that A PD entity has been translated to a PI one, and the
+ * mapping between the two entities has been learned by the system.
+ */
+ LEARNED,
+
+ /**
+ * Signals that a previously learned mapping between a PD entity and its
+ * PI counterpart has been removed.
+ */
+ FORGOT,
+ }
+
+ /**
+ * Creates a new translation event.
+ *
+ * @param type type of event
+ * @param subject subject of event
+ */
+ public PiTranslationEvent(Type type, PiTranslatedEntity subject) {
+ super(type, subject);
+ }
+}
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 210b7e0..6af3e83 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
@@ -20,40 +20,68 @@
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.group.Group;
import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiTableEntry;
+import java.util.Optional;
+
/**
- * A service to translate protocol-dependent entities to protocol-independent ones.
+ * A service to translate protocol-dependent (PD) entities to
+ * protocol-independent (PI) ones.
*/
@Beta
public interface PiTranslationService {
/**
- * Returns a PI table entry equivalent to the given flow rule for the given protocol-independent pipeline
- * configuration.
+ * Returns a PI table entry equivalent to the given flow rule for the given
+ * protocol-independent pipeline configuration.
*
* @param rule a flow rule
* @param pipeconf a pipeline configuration
* @return a table entry
* @throws PiTranslationException if the flow rule cannot be translated
*/
- PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf)
+ PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf)
throws PiTranslationException;
/**
- * Returns a PI action group equivalent to the given group for the given protocol-independent pipeline
- * configuration.
+ * Returns a PI action group equivalent to the given group for the given
+ * protocol-independent pipeline configuration.
*
* @param group a group
* @param pipeconf a pipeline configuration
* @return a PI action group
* @throws PiTranslationException if the group cannot be translated
*/
- PiActionGroup translateGroup(Group group, PiPipeconf pipeconf)
+ PiActionGroup translate(Group group, PiPipeconf pipeconf)
throws PiTranslationException;
/**
+ * Returns a flow rule previously translated to the given PI table entry,
+ * for the given pipeconf ID, if present. If not present it means that such
+ * flow rule was never translated in the first place.
+ *
+ * @param piTableEntry PI table entry
+ * @param pipeconfId pipeconf ID
+ * @return optional flow rule
+ */
+ Optional<FlowRule> lookup(PiTableEntry piTableEntry,
+ PiPipeconfId pipeconfId);
+
+ /**
+ * Returns a group previously translated to the given PI action group, for
+ * the given pipeconf ID, if present. If not present it means that such
+ * group was never translated in the first place.
+ *
+ * @param piActionGroup PI action group
+ * @param pipeconfId pipeconf ID
+ * @return optional group
+ */
+ Optional<Group> lookup(PiActionGroup piActionGroup,
+ PiPipeconfId pipeconfId);
+
+ /**
* Signals that an error was encountered while translating an entity.
*/
class PiTranslationException extends Exception {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java
new file mode 100644
index 0000000..6e0dec7
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.store.Store;
+
+/**
+ * PI translation service store abstraction that acts as a multi-language
+ * dictionary. For each pipeconf ID (language) it maintains a mapping between a
+ * protocol-dependent (PD) entity and an equivalent protocol-independent (PI)
+ * one.
+ */
+@Beta
+public interface PiTranslationStore
+ extends Store<PiTranslationEvent, PiTranslationStoreDelegate> {
+
+ /**
+ * Adds or update a mapping between the given PD entity (original) and the
+ * translated PI counterpart, for the given pipeconf ID.
+ *
+ * @param original PD entity
+ * @param translated PI entity
+ * @param pipeconfId pipeconf ID
+ */
+ void addOrUpdate(PiTranslatable original, PiEntity translated,
+ PiPipeconfId pipeconfId);
+
+ /**
+ * Removes a previously added mapping for the given PI entity and pipeconf
+ * ID.
+ *
+ * @param piEntity PI entity
+ * @param pipeconfId pipeconf ID
+ */
+ void remove(PiEntity piEntity, PiPipeconfId pipeconfId);
+
+ /**
+ * Removes all previously learned mappings for the given pipeconf ID.
+ *
+ * @param pipeconfId pipeconf ID
+ */
+ void removeAll(PiPipeconfId pipeconfId);
+
+ /**
+ * Returns a PD entity for the given PI one and pipeconf ID. Returns null if
+ * this store does not contain a mapping between the two for the given
+ * pipeconf ID.
+ *
+ * @param piEntity PI entity
+ * @param pipeconfId pipeconf ID
+ * @return PD entity or null
+ */
+ PiTranslatable lookup(PiEntity piEntity, PiPipeconfId pipeconfId);
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java
new file mode 100644
index 0000000..0fe5c75
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * PI translation service store delegate abstraction.
+ */
+@Beta
+public interface PiTranslationStoreDelegate
+ extends StoreDelegate<PiTranslationEvent> {
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index 3cb0a09..b6d90f0 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -28,12 +28,17 @@
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.group.Group;
import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.service.PiTranslatable;
import org.onosproject.net.pi.service.PiTranslationService;
+import org.onosproject.net.pi.service.PiTranslationStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Optional;
+
/**
* Implementation of the protocol-independent translation service.
*/
@@ -48,6 +53,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PiTranslationStore translationStore;
+
@Activate
public void activate() {
log.info("Started");
@@ -59,19 +67,43 @@
}
@Override
- public PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf) throws PiTranslationException {
- return PiFlowRuleTranslator.translate(rule, pipeconf, getDevice(rule.deviceId()));
+ public PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf)
+ throws PiTranslationException {
+ final PiTableEntry piTableEntry = PiFlowRuleTranslator
+ .translate(rule, pipeconf, getDevice(rule.deviceId()));
+ translationStore.addOrUpdate(rule, piTableEntry, pipeconf.id());
+ return piTableEntry;
}
@Override
- public PiActionGroup translateGroup(Group group, PiPipeconf pipeconf) throws PiTranslationException {
- return PiGroupTranslator.translate(group, pipeconf, getDevice(group.deviceId()));
+ public Optional<FlowRule> lookup(PiTableEntry piTableEntry,
+ PiPipeconfId pipeconfId) {
+ final PiTranslatable original = translationStore
+ .lookup(piTableEntry, pipeconfId);
+ return original == null
+ ? Optional.empty()
+ : Optional.of((FlowRule) original);
+ }
+
+ @Override
+ public PiActionGroup translate(Group group, PiPipeconf pipeconf)
+ throws PiTranslationException {
+ return PiGroupTranslator.translate(group, pipeconf,
+ getDevice(group.deviceId()));
+ }
+
+ @Override
+ public Optional<Group> lookup(PiActionGroup piActionGroup,
+ PiPipeconfId pipeconfId) {
+ // TODO: implement learning and lookup of groups
+ return Optional.empty();
}
private Device getDevice(DeviceId deviceId) throws PiTranslationException {
final Device device = deviceService.getDevice(deviceId);
if (device == null) {
- throw new PiTranslationException("Unable to get device " + deviceId);
+ throw new PiTranslationException(
+ "Unable to get device " + deviceId);
}
return device;
}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiTranslationStore.java b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiTranslationStore.java
new file mode 100644
index 0000000..f19c746
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiTranslationStore.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.pi.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationEvent;
+import org.onosproject.net.pi.service.PiTranslationStore;
+import org.onosproject.net.pi.service.PiTranslationStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Distributed implementation of PiTranslationStore.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedPiTranslationStore
+ extends AbstractStore<PiTranslationEvent, PiTranslationStoreDelegate>
+ implements PiTranslationStore {
+
+ private static final String DIST_MAP_NAME = "onos-pi-translated-entities-map";
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private EventuallyConsistentMap<PiTranslatedEntityKey, PiTranslatedEntity>
+ translatedEntities;
+
+ private final EventuallyConsistentMapListener<PiTranslatedEntityKey,
+ PiTranslatedEntity> entityMapListener = new InternalEntityMapListener();
+
+ @Activate
+ public void activate() {
+ translatedEntities = storageService
+ .<PiTranslatedEntityKey, PiTranslatedEntity>eventuallyConsistentMapBuilder()
+ .withName(DIST_MAP_NAME)
+ .withSerializer(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(PiTranslatedEntityKey.class)
+ .build())
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+ translatedEntities.addListener(entityMapListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ translatedEntities.removeListener(entityMapListener);
+ translatedEntities = null;
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addOrUpdate(PiTranslatable original, PiEntity translated,
+ PiPipeconfId pipeconfId) {
+ translatedEntities.put(PiTranslatedEntityKey.of(pipeconfId, translated),
+ new PiTranslatedEntity(original, translated, pipeconfId));
+ }
+
+ @Override
+ public void remove(PiEntity piEntity, PiPipeconfId pipeconfId) {
+ translatedEntities.remove(
+ PiTranslatedEntityKey.of(pipeconfId, piEntity));
+ }
+
+ @Override
+ public void removeAll(PiPipeconfId pipeconfId) {
+ // FIXME: this can be heavy, but we assume it won't be called that often
+ // How often we expect a pipeconf to be removed from the device/system?
+ final Set<PiTranslatedEntityKey> keysToRemove = translatedEntities
+ .keySet().parallelStream()
+ .filter(k -> k.pipeconfId.equals(pipeconfId))
+ .collect(Collectors.toSet());
+ keysToRemove.forEach(translatedEntities::remove);
+ }
+
+ @Override
+ public PiTranslatable lookup(PiEntity piEntity, PiPipeconfId pipeconfId) {
+ PiTranslatedEntity translatedEntity = translatedEntities
+ .get(PiTranslatedEntityKey.of(pipeconfId, piEntity));
+ return translatedEntity == null ? null : translatedEntity.original();
+ }
+
+
+ private class InternalEntityMapListener
+ implements EventuallyConsistentMapListener
+ <PiTranslatedEntityKey, PiTranslatedEntity> {
+
+ @Override
+ public void event(EventuallyConsistentMapEvent<PiTranslatedEntityKey,
+ PiTranslatedEntity> event) {
+ final PiTranslationEvent.Type type;
+ switch (event.type()) {
+ case PUT:
+ type = PiTranslationEvent.Type.LEARNED;
+ break;
+ case REMOVE:
+ type = PiTranslationEvent.Type.FORGOT;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown event type " + event.type().name());
+ }
+ notifyDelegate(new PiTranslationEvent(type, event.value()));
+ }
+ }
+
+ /**
+ * Internal representation of a key that uniquely identifies a translated
+ * entity.
+ */
+ private static final class PiTranslatedEntityKey {
+
+ private final PiPipeconfId pipeconfId;
+ private final PiEntity piEntity;
+
+ private PiTranslatedEntityKey(PiPipeconfId pipeconfId,
+ PiEntity piEntity) {
+ this.pipeconfId = pipeconfId;
+ this.piEntity = piEntity;
+ }
+
+ public static PiTranslatedEntityKey of(PiPipeconfId pipeconfId,
+ PiEntity piEntity) {
+ return new PiTranslatedEntityKey(pipeconfId, piEntity);
+ }
+
+ public static PiTranslatedEntityKey of(PiTranslatedEntity entity) {
+ return new PiTranslatedEntityKey(entity.pipeconfId(),
+ entity.translated());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pipeconfId, piEntity);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final PiTranslatedEntityKey other = (PiTranslatedEntityKey) obj;
+ return Objects.equals(this.pipeconfId, other.pipeconfId)
+ && Objects.equals(this.piEntity, other.piEntity);
+ }
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index cc3c05d..9c5cb82 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -227,6 +227,8 @@
import org.onosproject.net.pi.runtime.PiControlMetadata;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiEntityType;
import org.onosproject.net.pi.runtime.PiExactFieldMatch;
import org.onosproject.net.pi.runtime.PiFieldMatch;
import org.onosproject.net.pi.runtime.PiGroupKey;
@@ -239,6 +241,8 @@
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.net.pi.runtime.PiValidFieldMatch;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.region.DefaultRegion;
import org.onosproject.net.region.Region;
@@ -637,6 +641,8 @@
PiControlMetadata.class,
PiCounterCellData.class,
PiCounterCellId.class,
+ PiEntity.class,
+ PiEntityType.class,
PiExactFieldMatch.class,
PiFieldMatch.class,
PiGroupKey.class,
@@ -649,6 +655,9 @@
PiTableEntry.class,
PiTernaryFieldMatch.class,
PiValidFieldMatch.class,
+ // PI service
+ PiTranslatedEntity.class,
+ PiTranslatable.class,
// Other
PiCriterion.class,
PiInstruction.class
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index 5f95eda..b216fcf 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -242,7 +242,7 @@
PiTableEntry piTableEntry;
try {
- piTableEntry = piTranslationService.translateFlowRule(rule, pipeconf);
+ piTableEntry = piTranslationService.translate(rule, pipeconf);
} catch (PiTranslationService.PiTranslationException e) {
log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
continue; // next rule
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 464c4b6..a3139bc 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
@@ -103,7 +103,7 @@
PiActionGroup piActionGroup;
try {
- piActionGroup = piTranslationService.translateGroup(group, pipeconf);
+ piActionGroup = piTranslationService.translate(group, pipeconf);
} catch (PiTranslationService.PiTranslationException e) {
log.warn("Unable translate group, aborting group operation {}: {}", groupOp.opType(), e.getMessage());
return;