[AETHER-1295][AETHER-1294] First stab to the TOST APIs framework
and to the DROP policies.

Patch includes also some CLI commands. REST APIs will be implemented
in a separate review. Other review will follow to implement the logic
of the REDIRECT policies

Change-Id: I34aa3da700c5a16682196e4dd8db9c4757d609c4
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/AbstractPolicy.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/AbstractPolicy.java
new file mode 100644
index 0000000..4be54a7
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/AbstractPolicy.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+/**
+ * Abstract implementation of the policy interface.
+ */
+public abstract class AbstractPolicy implements Policy {
+    // Shared state among policies
+    protected PolicyId policyId;
+    private PolicyType policyType;
+
+    /**
+     * Init the basic information of a policy.
+     *
+     * @param pType the policy type
+     */
+    protected AbstractPolicy(PolicyType pType) {
+        policyType = pType;
+    }
+
+    @Override
+    public PolicyId policyId() {
+        return policyId;
+    }
+
+    @Override
+    public PolicyType policyType() {
+        return policyType;
+    }
+
+    protected abstract PolicyId computePolicyId();
+
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicy.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicy.java
new file mode 100644
index 0000000..ac2e05e
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicy.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Implementation of the drop policy.
+ */
+public final class DropPolicy extends AbstractPolicy {
+    /**
+     * Builds up a DROP policy.
+     */
+    public DropPolicy() {
+        super(PolicyType.DROP);
+        policyId = computePolicyId();
+    }
+
+    @Override
+    protected PolicyId computePolicyId() {
+        return PolicyId.of(policyType().name());
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DropPolicy)) {
+            return false;
+        }
+        final DropPolicy other = (DropPolicy) obj;
+        return Objects.equals(policyType(), other.policyType()) &&
+                Objects.equals(policyId(), other.policyId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(policyId(), policyType());
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("policyId", policyId())
+                .add("policyType", policyType())
+                .toString();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/Policy.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/Policy.java
new file mode 100644
index 0000000..bc1acf0
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/Policy.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+/**
+ * Represents a policy in TOST.
+ */
+public interface Policy {
+    /**
+     * Represents the type of a policy.
+     */
+    enum PolicyType {
+        /**
+         * The policy drops the associated traffic.
+         */
+        DROP,
+
+        /**
+         * The policy redirects traffic using custom routing.
+         */
+        REDIRECT
+    }
+
+    /**
+     * Returns the policy id.
+     *
+     * @return the policy id
+     */
+    PolicyId policyId();
+
+    /**
+     * Returns the policy type.
+     *
+     * @return the type of a policy
+     */
+    PolicyType policyType();
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyData.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyData.java
new file mode 100644
index 0000000..5e207ae
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyData.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import java.util.List;
+
+/**
+ * Policy data retrieved from the system.
+ */
+public final class PolicyData {
+    // We want to provide access to the policy data as well as
+    // the policy operations in the system and their status
+    private final PolicyState policyState;
+    private final Policy policy;
+    private final List<String> operations;
+
+    /**
+     * Creates a policy data.
+     *
+     * @param pState the policy state
+     * @param pol the policy
+     * @param ops the operations associated
+     */
+    public PolicyData(PolicyState pState, Policy pol, List<String> ops) {
+        policy = pol;
+        policyState = pState;
+        operations = ops;
+    }
+
+    /**
+     * Returns the current state of the policy.
+     *
+     * @return the policy state
+     */
+    public PolicyState policyState() {
+        return policyState;
+    }
+
+
+    /**
+     * Returns the policy associated.
+     *
+     * @return the policy
+     */
+    public Policy policy() {
+        return policy;
+    }
+
+    /**
+     * Returns the operations in the system in form of strings.
+     *
+     * @return the operations
+     */
+    public List<String> operations() {
+        return operations;
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyId.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyId.java
new file mode 100644
index 0000000..e3e0cc1
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyId.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a policy id.
+ */
+public final class PolicyId extends Identifier<String> {
+
+    protected PolicyId(String id) {
+        super(id);
+    }
+
+    /**
+     * Returns the id of the policy given the value.
+     *
+     * @param name policy id value
+     * @return policy id
+     */
+    public static PolicyId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name cannot be empty");
+        return new PolicyId(name);
+    }
+
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyService.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyService.java
new file mode 100644
index 0000000..2e188ab
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import java.util.Set;
+
+/**
+ * Policies to drop, reroute, apply QoS and overlay the traffic.
+ */
+public interface PolicyService {
+    /**
+     * Traffic match priority.
+     */
+    int TRAFFIC_MATCH_PRIORITY = 60000;
+
+    /**
+     * Creates or updates a policy.
+     *
+     * @param policy the policy to create
+     * @return the id of the policy being created. Otherwise null.
+     */
+    PolicyId addOrUpdatePolicy(Policy policy);
+
+    /**
+     * Issues a policy removal.
+     *
+     * @param policyId the id of the policy to remove
+     * @return whether or not the operation was successful
+     */
+    boolean removePolicy(PolicyId policyId);
+
+    /**
+     * Returns a set of policies. The policy types can be used
+     * as filter.
+     *
+     * @param filter the policy types
+     * @return the policies stored in the system observing
+     * the filtering rule
+     */
+    Set<PolicyData> policies(Set<Policy.PolicyType> filter);
+
+    /**
+     * Attaches a traffic match to a policy.
+     *
+     * @param trafficMatch the traffic match
+     * @return the traffic match id or null if not successful
+     */
+    TrafficMatchId addOrUpdateTrafficMatch(TrafficMatch trafficMatch);
+
+    /**
+     * Issues a traffic match removal.
+     *
+     * @param trafficMatchId the id of the traffic match to remove
+     * @return whether or not the operation was successful
+     */
+    boolean removeTrafficMatch(TrafficMatchId trafficMatchId);
+
+    /**
+     * Returns a set of traffic matches.
+     *
+     * @return the traffic matches stored in the system
+     */
+    Set<TrafficMatchData> trafficMatches();
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyState.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyState.java
new file mode 100644
index 0000000..ad767ec
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/PolicyState.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+/**
+ * Represents the state of a policy as seen by the system.
+ */
+public enum PolicyState {
+    /**
+     * The policy is in the process of being added.
+     */
+    PENDING_ADD,
+
+    /**
+     * The policy has been added.
+     */
+    ADDED,
+
+    /**
+     * The policy is in the process of being removed.
+     */
+    PENDING_REMOVE
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatch.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatch.java
new file mode 100644
index 0000000..83098fe
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatch.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+
+import com.google.common.hash.Funnel;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import org.onosproject.net.flow.TrafficSelector;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Representation of a traffic match.
+ */
+public final class TrafficMatch {
+    // Traffic match internal state
+    private TrafficMatchId trafficMatchId;
+    private TrafficSelector trafficSelector;
+    private PolicyId policyId;
+
+    /**
+     * Builds a traffic match.
+     *
+     * @param trafficselector the traffic selector
+     * @param policyid the associated policy id
+     */
+    public TrafficMatch(TrafficSelector trafficselector, PolicyId policyid) {
+        trafficSelector = trafficselector;
+        trafficMatchId = TrafficMatchId.trafficMatchId(computeTrafficMatchId());
+        policyId = policyid;
+    }
+
+    /**
+     * Returns the traffic match id.
+     *
+     * @return the id of the traffic match
+     */
+    public TrafficMatchId trafficMatchId() {
+        return trafficMatchId;
+    }
+
+    /**
+     * Returns the id of the policy associated with.
+     *
+     * @return the policy id
+     */
+    public PolicyId policyId() {
+        return policyId;
+    }
+
+    /**
+     * Returns the traffic selector associated with.
+     *
+     * @return the traffic selector
+     */
+    public TrafficSelector trafficSelector() {
+        return trafficSelector;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(trafficMatchId, trafficSelector, policyId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TrafficMatch) {
+            final TrafficMatch other = (TrafficMatch) obj;
+            return Objects.equals(this.trafficMatchId, other.trafficMatchId) &&
+                    Objects.equals(trafficSelector, other.trafficSelector) &&
+                    Objects.equals(policyId, other.policyId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("trafficMatchId", trafficMatchId)
+                .add("trafficSelector", trafficSelector)
+                .add("policyId", policyId)
+                .toString();
+    }
+
+    // Compute the id using the traffic selector. This method results to be consistent across the cluster.
+    private int computeTrafficMatchId() {
+        Funnel<TrafficSelector> selectorFunnel = (from, into) -> from.criteria()
+                .forEach(c -> into.putUnencodedChars(c.toString()));
+        HashFunction hashFunction = Hashing.murmur3_32();
+        HashCode hashCode = hashFunction.newHasher()
+                .putObject(trafficSelector, selectorFunnel)
+                .hash();
+        return hashCode.asInt();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchData.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchData.java
new file mode 100644
index 0000000..3932b4c
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchData.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import java.util.List;
+
+/**
+ * Traffic match data retrieved from the system.
+ */
+public final class TrafficMatchData {
+    // We want to provide access to the traffic match data as well as
+    // the traffic match operations in the system and their status
+    private final TrafficMatchState trafficMatchState;
+    private final TrafficMatch trafficMatch;
+    private final List<String> operations;
+
+
+    public TrafficMatchData(TrafficMatchState tState, TrafficMatch tMatch, List<String> ops) {
+        trafficMatch = tMatch;
+        trafficMatchState = tState;
+        operations = ops;
+    }
+
+    /**
+     * Returns the current state of the traffic match.
+     *
+     * @return the traffic match state
+     */
+    public TrafficMatchState trafficMatchState() {
+        return trafficMatchState;
+    }
+
+
+    /**
+     * Returns the traffic match associated.
+     *
+     * @return the traffic match
+     */
+    public TrafficMatch trafficMatch() {
+        return trafficMatch;
+    }
+
+    /**
+     * Returns the operations in the system in form of strings.
+     *
+     * @return the operations
+     */
+    public List<String> operations() {
+        return operations;
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchId.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchId.java
new file mode 100644
index 0000000..5833f66
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchId.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a traffic match id.
+ */
+public final class TrafficMatchId extends Identifier<Integer> {
+
+    protected TrafficMatchId(int value) {
+        super(value);
+    }
+
+    /**
+     * Converts an int into a traffic match id.
+     *
+     * @param value the value of the id
+     * @return the traffic match id
+     */
+    public static TrafficMatchId trafficMatchId(int value) {
+        return new TrafficMatchId(value);
+    }
+
+    /**
+     * Returns the id of the traffic match given the value.
+     *
+     * @param name traffic match id value
+     * @return traffic match id
+     */
+    public static TrafficMatchId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name cannot be empty");
+        return new TrafficMatchId(Integer.parseInt(name));
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(this.identifier);
+    }
+
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchState.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchState.java
new file mode 100644
index 0000000..f775c50
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchState.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+/**
+ * Represents the state of a traffic match as seen by the system.
+ */
+public enum TrafficMatchState {
+    /**
+     * The traffic match is in the process of being added.
+     */
+    PENDING_ADD,
+
+    /**
+     * The traffic match has been added.
+     */
+    ADDED,
+
+    /**
+     * The traffic match is in the process of being removed.
+     */
+    PENDING_REMOVE
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/package-info.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/package-info.java
new file mode 100644
index 0000000..cfbaa28
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Policy API.
+ */
+package org.onosproject.segmentrouting.policy.api;
\ No newline at end of file
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/Operation.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/Operation.java
new file mode 100644
index 0000000..99c9fd4
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/Operation.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import com.google.common.base.MoreObjects;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.segmentrouting.policy.api.Policy;
+import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * keep track of the operation and of its status in the system.
+ */
+final class Operation {
+    private boolean isInstall;
+    private boolean isDone;
+    private Objective objectiveOperation;
+    private Policy policy;
+    private TrafficMatch trafficMatch;
+
+    private Operation(boolean install, boolean done, Objective objective,
+                      Policy pol, TrafficMatch tMatch) {
+        isInstall = install;
+        isDone = done;
+        objectiveOperation = objective;
+        policy = pol;
+        trafficMatch = tMatch;
+    }
+
+    /**
+     * Returns whether or not the operation is done.
+     *
+     * @return true if operation is done. False otherwise.
+     */
+    public boolean isDone() {
+        return isDone;
+    }
+
+    /**
+     * Returns whether or not it is an installation.
+     *
+     * @return true if it is an installation. False for removal
+     */
+    public boolean isInstall() {
+        return isInstall;
+    }
+
+    /**
+     * Returns the objective operation.
+     *
+     * @return the associated flow objective
+     */
+    public Objective objectiveOperation() {
+        return objectiveOperation;
+    }
+
+    /**
+     * Returns the policy if present.
+     *
+     * @return the policy
+     */
+    public Optional<Policy> policy() {
+        return Optional.ofNullable(policy);
+    }
+
+    /**
+     * Returns the traffic match if present.
+     *
+     * @return the traffic match
+     */
+    public Optional<TrafficMatch> trafficMatch() {
+        return Optional.ofNullable(trafficMatch);
+    }
+
+    /**
+     * Updates isDone.
+     *
+     * @param isDone if it is done
+     */
+    public void isDone(boolean isDone) {
+        this.isDone = isDone;
+    }
+
+    /**
+     * Updates the flowObjective.
+     *
+     * @param objectiveOperation the flowObjective
+     */
+    public void objectiveOperation(Objective objectiveOperation) {
+        this.objectiveOperation = objectiveOperation;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof Operation)) {
+            return false;
+        }
+        final Operation other = (Operation) obj;
+        return  this.isInstall == other.isInstall &&
+                this.isDone == other.isDone &&
+                Objects.equals(this.objectiveOperation, other.objectiveOperation) &&
+                Objects.equals(this.policy, other.policy) &&
+                Objects.equals(this.trafficMatch, other.trafficMatch);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(isInstall, isDone, objectiveOperation, policy, trafficMatch);
+    }
+
+    @Override
+    public String toString() {
+        MoreObjects.ToStringHelper helper = toStringHelper(this)
+                .add("isInstall", isInstall)
+                .add("isDone", isDone)
+                .add("objective", objectiveOperation);
+        if (policy != null) {
+            helper.add("policy", policy);
+        }
+        if (trafficMatch != null) {
+            helper.add("trafficMatch", trafficMatch);
+        }
+        return helper.toString();
+    }
+
+    public String toStringMinimal() {
+        MoreObjects.ToStringHelper helper = toStringHelper(this)
+                .add("isInstall", isInstall)
+                .add("isDone", isDone);
+        if (policy != null) {
+            helper.add("policy", policy);
+        }
+        if (trafficMatch != null) {
+            helper.add("trafficMatch", trafficMatch);
+        }
+        return helper.toString();
+    }
+
+    /**
+     * Creates a new operation builder.
+     *
+     * @return an operation builder
+     */
+    public static Operation.Builder builder() {
+        return new Operation.Builder();
+    }
+
+    /**
+     * Creates a new operation builder using the supplied operation.
+     *
+     * @param operation the operation
+     * @return an operation builder
+     */
+    public static Operation.Builder builder(Operation operation) {
+        return new Operation.Builder(operation);
+    }
+
+    /**
+     * Builder for Operation objects.
+     */
+    public static final class Builder {
+        private boolean isInstall;
+        private boolean isDone;
+        private Objective objectiveOperation;
+        private Policy policy;
+        private TrafficMatch trafficMatch;
+
+        private Builder() {
+            // Hide constructor
+        }
+
+        private Builder(Operation operation) {
+            isInstall = operation.isInstall();
+            isDone = operation.isDone();
+            objectiveOperation = operation.objectiveOperation();
+            policy = operation.policy().orElse(null);
+            trafficMatch = operation.trafficMatch().orElse(null);
+        }
+
+        /**
+         * Sets the flowObjective.
+         *
+         * @param objectiveOperation the flowObjective
+         * @return this builder
+         */
+        public Builder objectiveOperation(Objective objectiveOperation) {
+            this.objectiveOperation = objectiveOperation;
+            return this;
+        }
+
+        /**
+         * Sets if it is done.
+         *
+         * @param isDone if it is done
+         * @return this builder
+         */
+        public Builder isDone(boolean isDone) {
+            this.isDone = isDone;
+            return this;
+        }
+
+        /**
+         * Sets if it is an installation.
+         *
+         * @param isInstall if it is an installation
+         * @return this builder
+         */
+        public Builder isInstall(boolean isInstall) {
+            this.isInstall = isInstall;
+            return this;
+        }
+
+        /**
+         * Sets the policy.
+         *
+         * @param policy the policy
+         * @return this builder
+         */
+        public Builder policy(Policy policy) {
+            this.policy = policy;
+            return this;
+        }
+
+        /**
+         * Sets the traffic match.
+         *
+         * @param trafficMatch the traffic match
+         * @return this builder
+         */
+        public Builder trafficMatch(TrafficMatch trafficMatch) {
+            this.trafficMatch = trafficMatch;
+            return this;
+        }
+
+        /**
+         * Builds an operation object from the accumulated parameters.
+         *
+         * @return operation object
+         */
+        public Operation build() {
+            if ((policy == null && trafficMatch == null) ||
+                    (policy != null && trafficMatch != null)) {
+                throw new IllegalArgumentException("Policy and traffic cannot be both null or both set");
+            }
+            return new Operation(isInstall, isDone, objectiveOperation, policy, trafficMatch);
+        }
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyKey.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyKey.java
new file mode 100644
index 0000000..e0eac06
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyKey.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * Policy key used by the store.
+ */
+public class PolicyKey {
+    private DeviceId deviceId;
+    private PolicyId policyId;
+
+    /**
+     * Constructs new policy key with given device id and policy id.
+     *
+     * @param deviceId device id
+     * @param policyId policy id
+     */
+    public PolicyKey(DeviceId deviceId, PolicyId policyId) {
+        this.deviceId = deviceId;
+        this.policyId = policyId;
+    }
+
+    /**
+     * Gets device id.
+     *
+     * @return device id of the policy key
+     */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    /**
+     * Gets policy id.
+     *
+     * @return the id of the policy
+     */
+    public PolicyId policyId() {
+        return policyId;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PolicyKey)) {
+            return false;
+        }
+        final PolicyKey other = (PolicyKey) obj;
+        return Objects.equals(this.deviceId, other.deviceId) &&
+                Objects.equals(this.policyId, other.policyId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(deviceId, policyId);
+    }
+
+    /**
+     * Parses from a string the police key.
+     *
+     * @param str the string to parse
+     * @return the policy key if present in the str, null otherwise
+     */
+    public static PolicyKey fromString(String str) {
+        PolicyKey policyKey = null;
+        if (str != null && str.contains(PolicyManager.KEY_SEPARATOR)) {
+            StringTokenizer tokenizer = new StringTokenizer(str, PolicyManager.KEY_SEPARATOR);
+            if (tokenizer.countTokens() == 2) {
+                policyKey = new PolicyKey(DeviceId.deviceId(tokenizer.nextToken()),
+                        PolicyId.of(tokenizer.nextToken()));
+            }
+        }
+        return policyKey;
+    }
+
+    @Override
+    public String toString() {
+        return deviceId.toString() + PolicyManager.KEY_SEPARATOR + policyId.toString();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java
new file mode 100644
index 0000000..dbd5667
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import org.glassfish.jersey.internal.guava.Sets;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.PredictableExecutor;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultObjectiveContext;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.intent.WorkPartitionService;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.policy.api.DropPolicy;
+import org.onosproject.segmentrouting.policy.api.Policy;
+import org.onosproject.segmentrouting.policy.api.Policy.PolicyType;
+import org.onosproject.segmentrouting.policy.api.PolicyData;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+import org.onosproject.segmentrouting.policy.api.PolicyState;
+import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchData;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchState;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageException;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the policy service interface.
+ */
+@Component(immediate = true, service = PolicyService.class)
+public class PolicyManager implements PolicyService {
+
+    // App related things
+    private static final String APP_NAME = "org.onosproject.segmentrouting.policy";
+    private ApplicationId appId;
+    private Logger log = getLogger(getClass());
+    static final String KEY_SEPARATOR = "|";
+
+    // Policy/TrafficMatch store related objects. We use these consistent maps to keep track of the
+    // lifecycle of a policy/traffic match. These are decomposed in multiple operations which have
+    // to be performed on multiple devices in order to have a policy/traffic match in ADDED state.
+    private static final String POLICY_STORE = "sr-policy-store";
+    private ConsistentMap<PolicyId, PolicyRequest> policies;
+    private MapEventListener<PolicyId, PolicyRequest> mapPolListener = new InternalPolMapEventListener();
+    private Map<PolicyId, PolicyRequest> policiesMap;
+
+    private static final String OPS_STORE = "sr-ops-store";
+    private ConsistentMap<String, Operation> operations;
+    private MapEventListener<String, Operation> mapOpsListener = new InternalOpsMapEventListener();
+    private Map<String, Operation> opsMap;
+
+    private static final String TRAFFIC_MATCH_STORE = "sr-tmatch-store";
+    private ConsistentMap<TrafficMatchId, TrafficMatchRequest> trafficMatches;
+    private MapEventListener<TrafficMatchId, TrafficMatchRequest> mapTMatchListener =
+            new InternalTMatchMapEventListener();
+    private Map<TrafficMatchId, TrafficMatchRequest> trafficMatchesMap;
+
+    // Leadership related objects - consistent hashing
+    private static final HashFunction HASH_FN = Hashing.md5();
+    // Read only cache of the Policy leader
+    private Map<PolicyId, NodeId> policyLeaderCache;
+
+    // Worker threads for policy and traffic match related ops
+    private static final int DEFAULT_THREADS = 4;
+    protected PredictableExecutor workers;
+
+    // Serializers and ONOS services
+    private static final KryoNamespace.Builder APP_KRYO_BUILDER = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(PolicyId.class)
+            .register(PolicyType.class)
+            .register(DropPolicy.class)
+            .register(PolicyState.class)
+            .register(PolicyRequest.class)
+            .register(TrafficMatchId.class)
+            .register(TrafficMatchState.class)
+            .register(TrafficMatch.class)
+            .register(TrafficMatchRequest.class)
+            .register(Operation.class);
+    private Serializer serializer = Serializer.using(Lists.newArrayList(APP_KRYO_BUILDER.build()));
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    public WorkPartitionService workPartitionService;
+
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
+    public SegmentRoutingService srService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    public FlowObjectiveService flowObjectiveService;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication(APP_NAME);
+
+        policies = storageService.<PolicyId, PolicyRequest>consistentMapBuilder()
+                .withName(POLICY_STORE)
+                .withSerializer(serializer).build();
+        policies.addListener(mapPolListener);
+        policiesMap = policies.asJavaMap();
+
+        trafficMatches = storageService.<TrafficMatchId, TrafficMatchRequest>consistentMapBuilder()
+                .withName(TRAFFIC_MATCH_STORE)
+                .withSerializer(serializer).build();
+        trafficMatches.addListener(mapTMatchListener);
+        trafficMatchesMap = trafficMatches.asJavaMap();
+
+        operations = storageService.<String, Operation>consistentMapBuilder()
+                .withName(OPS_STORE)
+                .withSerializer(serializer).build();
+        operations.addListener(mapOpsListener);
+        opsMap = operations.asJavaMap();
+
+        policyLeaderCache = Maps.newConcurrentMap();
+
+        workers = new PredictableExecutor(DEFAULT_THREADS,
+                groupedThreads("sr-policy", "worker-%d", log));
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        // Teardown everything
+        policies.removeListener(mapPolListener);
+        policies.destroy();
+        policiesMap.clear();
+        trafficMatches.removeListener(mapTMatchListener);
+        trafficMatches.destroy();
+        trafficMatchesMap.clear();
+        operations.removeListener(mapOpsListener);
+        operations.destroy();
+        operations.clear();
+        workers.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    //FIXME update does not work well
+    public PolicyId addOrUpdatePolicy(Policy policy) {
+        PolicyId policyId = policy.policyId();
+        try {
+            policies.put(policyId, new PolicyRequest(policy));
+        } catch (StorageException e) {
+            log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
+                    e.getMessage(), e);
+            policyId = null;
+        }
+        return policyId;
+    }
+
+    @Override
+    public boolean removePolicy(PolicyId policyId) {
+        boolean result;
+        try {
+            result = Versioned.valueOrNull(policies.computeIfPresent(policyId, (k, v) -> {
+                if (v.policyState() != PolicyState.PENDING_REMOVE) {
+                    v.policyState(PolicyState.PENDING_REMOVE);
+                }
+                return v;
+            })) != null;
+        } catch (StorageException e) {
+            log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
+                    e.getMessage(), e);
+            result = false;
+        }
+        return result;
+    }
+
+    @Override
+    public Set<PolicyData> policies(Set<PolicyType> filter) {
+        Set<PolicyData> policyData = Sets.newHashSet();
+        List<DeviceId> edgeDeviceIds = srService.getEdgeDeviceIds();
+        Set<PolicyRequest> policyRequests;
+        if (filter.isEmpty()) {
+            policyRequests = ImmutableSet.copyOf(policiesMap.values());
+        } else {
+            policyRequests = policiesMap.values().stream()
+                    .filter(policyRequest -> filter.contains(policyRequest.policyType()))
+                    .collect(Collectors.toSet());
+        }
+        PolicyKey policyKey;
+        List<String> ops;
+        for (PolicyRequest policyRequest : policyRequests) {
+            ops = Lists.newArrayList();
+            for (DeviceId deviceId : edgeDeviceIds) {
+                policyKey = new PolicyKey(deviceId, policyRequest.policyId());
+                Operation operation = Versioned.valueOrNull(operations.get(policyKey.toString()));
+                if (operation != null) {
+                    ops.add(deviceId + " -> " + operation.toStringMinimal());
+                }
+            }
+            policyData.add(new PolicyData(policyRequest.policyState(), policyRequest.policy(), ops));
+        }
+        return policyData;
+    }
+
+    @Override
+    //FIXME update does not work well
+    public TrafficMatchId addOrUpdateTrafficMatch(TrafficMatch trafficMatch) {
+        TrafficMatchId trafficMatchId = trafficMatch.trafficMatchId();
+        try {
+            trafficMatches.put(trafficMatchId, new TrafficMatchRequest(trafficMatch));
+        } catch (StorageException e) {
+            log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
+                    e.getMessage(), e);
+            trafficMatchId = null;
+        }
+        return trafficMatchId;
+    }
+
+    @Override
+    public boolean removeTrafficMatch(TrafficMatchId trafficMatchId) {
+        boolean result;
+        try {
+            result = Versioned.valueOrNull(trafficMatches.computeIfPresent(trafficMatchId, (k, v) -> {
+                if (v.trafficMatchState() != TrafficMatchState.PENDING_REMOVE) {
+                    v.trafficMatchState(TrafficMatchState.PENDING_REMOVE);
+                }
+                return v;
+            })) != null;
+        } catch (StorageException e) {
+            log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
+                    e.getMessage(), e);
+            result = false;
+        }
+        return result;
+    }
+
+    @Override
+    public Set<TrafficMatchData> trafficMatches() {
+        Set<TrafficMatchData> trafficMatchData = Sets.newHashSet();
+        List<DeviceId> edgeDeviceIds = srService.getEdgeDeviceIds();
+        Set<TrafficMatchRequest> trafficMatchRequests = ImmutableSet.copyOf(trafficMatchesMap.values());
+        TrafficMatchKey trafficMatchKey;
+        List<String> ops;
+        for (TrafficMatchRequest trafficMatchRequest : trafficMatchRequests) {
+            ops = Lists.newArrayList();
+            for (DeviceId deviceId : edgeDeviceIds) {
+                trafficMatchKey = new TrafficMatchKey(deviceId, trafficMatchRequest.trafficMatch().trafficMatchId());
+                Operation operation = Versioned.valueOrNull(operations.get(trafficMatchKey.toString()));
+                if (operation != null) {
+                    ops.add(deviceId + " -> " + operation.toStringMinimal());
+                }
+            }
+            trafficMatchData.add(new TrafficMatchData(trafficMatchRequest.trafficMatchState(),
+                    trafficMatchRequest.trafficMatch(), ops));
+        }
+        return trafficMatchData;
+    }
+
+    // Install/remove the policies on the edge devices
+    private void sendPolicy(Policy policy, boolean install) {
+        if (!isLeader(policy.policyId())) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", policy.policyId());
+            }
+            return;
+        }
+        // We know that we are the leader, offloads to the workers the remaining
+        // part: issue fobj installation/removal and update the maps
+        List<DeviceId> edgeDeviceIds = srService.getEdgeDeviceIds();
+        for (DeviceId deviceId : edgeDeviceIds) {
+            workers.execute(() -> {
+                if (install) {
+                    installPolicyInDevice(deviceId, policy);
+                } else {
+                    removePolicyInDevice(deviceId, policy);
+                }
+            }, deviceId.hashCode());
+        }
+    }
+
+    // Orchestrate policy installation according to the type
+    private void installPolicyInDevice(DeviceId deviceId, Policy policy) {
+        PolicyKey policyKey;
+        Operation operation;
+        if (policy.policyType() == PolicyType.DROP) {
+            if (log.isDebugEnabled()) {
+                log.debug("Installing DROP policy {}", policy.policyId());
+            }
+            // DROP policies do not need the next objective installation phase
+            // we can update directly the map and signal the ops as done
+            policyKey = new PolicyKey(deviceId, policy.policyId());
+            operation = Operation.builder()
+                    .isDone(true)
+                    .isInstall(true)
+                    .policy(policy)
+                    .build();
+            operations.put(policyKey.toString(), operation);
+        } else if (policy.policyType() == PolicyType.REDIRECT) {
+            if (log.isDebugEnabled()) {
+                log.debug("Installing REDIRECT policy {}", policy.policyId());
+            }
+            // REDIRECT Uses objective context to update the ops as done when it returns
+            // successfully. In the other cases leaves the ops as undone and the
+            // relative policy will remain in pending.
+        } else {
+            log.warn("Policy {} type {} not yet supported",
+                    policy.policyId(), policy.policyType());
+        }
+    }
+
+    // Remove policy in a device according to the type
+    private void removePolicyInDevice(DeviceId deviceId, Policy policy) {
+        if (log.isDebugEnabled()) {
+            log.debug("Removing policy {}", policy.policyId());
+        }
+        PolicyKey policyKey = new PolicyKey(deviceId, policy.policyId());
+        Operation operation = Versioned.valueOrNull(operations.get(policyKey.toString()));
+        // Policy might be still in pending or not present anymore
+        if (operation == null || operation.objectiveOperation() == null) {
+            log.warn("There are no ops associated with {}", policyKey);
+            operation = Operation.builder()
+                    .isDone(true)
+                    .isInstall(false)
+                    .policy(policy)
+                    .build();
+            operations.put(policyKey.toString(), operation);
+        } else {
+            if (policy.policyType() == PolicyType.DROP) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing DROP policy {}", policy.policyId());
+                }
+                operation = Operation.builder()
+                        .isDone(true)
+                        .isInstall(false)
+                        .policy(policy)
+                        .build();
+                operations.put(policyKey.toString(), operation);
+            } else if (policy.policyType() == PolicyType.REDIRECT) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Removing REDIRECT policy {}", policy.policyId());
+                }
+                // REDIRECT has to remove first a next objective
+            } else {
+                log.warn("Policy {} type {} not yet supported",
+                        policy.policyId(), policy.policyType());
+            }
+        }
+    }
+
+    // Updates policy status if all the pending ops are done
+    private void updatePolicy(PolicyId policyId, boolean install) {
+        if (!isLeader(policyId)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", policyId);
+            }
+            return;
+        }
+        workers.execute(() -> updatePolicyInternal(policyId, install), policyId.hashCode());
+    }
+
+    private void updatePolicyInternal(PolicyId policyId, boolean install) {
+        // If there are no more pending ops we are ready to go; potentially we can check
+        // if the id is contained. Updates policies only if they are still present
+        Optional<Map.Entry<String, Versioned<Operation>>> notYetDone = operations.entrySet().stream()
+                .filter(entry -> entry.getValue().value().policy().isPresent())
+                .filter(entry -> PolicyKey.fromString(entry.getKey()).policyId().equals(policyId))
+                .filter(entry -> !entry.getValue().value().isDone() && entry.getValue().value().isInstall() == install)
+                .findFirst();
+        if (notYetDone.isEmpty()) {
+            PolicyRequest policyRequest = Versioned.valueOrNull(policies.computeIfPresent(policyId, (k, v) -> {
+               if (v.policyState() == PolicyState.PENDING_ADD && install)  {
+                   if (log.isDebugEnabled()) {
+                       log.debug("Policy {} is ready", policyId);
+                   }
+                   v.policyState(PolicyState.ADDED);
+               } else if (v.policyState() == PolicyState.PENDING_REMOVE && !install) {
+                   if (log.isDebugEnabled()) {
+                       log.debug("Policy {} is removed", policyId);
+                   }
+                   v = null;
+               }
+               return v;
+            }));
+            // Greedy check for pending traffic matches
+            if (policyRequest != null && policyRequest.policyState() == PolicyState.ADDED) {
+                updatePendingTrafficMatches(policyRequest.policyId());
+            }
+        }
+    }
+
+    // Install/remove the traffic match on the edge devices
+    private void sendTrafficMatch(TrafficMatch trafficMatch, boolean install) {
+        if (!isLeader(trafficMatch.policyId())) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", trafficMatch.policyId());
+            }
+            return;
+        }
+        // We know that we are the leader, offloads to the workers the remaining
+        // part: issue fobj installation/removal and update the maps
+        List<DeviceId> edgeDeviceIds = srService.getEdgeDeviceIds();
+        for (DeviceId deviceId : edgeDeviceIds) {
+            workers.execute(() -> {
+                if (install) {
+                    installTrafficMatchToDevice(deviceId, trafficMatch);
+                } else {
+                    removeTrafficMatchInDevice(deviceId, trafficMatch);
+                }
+            }, deviceId.hashCode());
+        }
+    }
+
+    // Orchestrate traffic match installation according to the type
+    private void installTrafficMatchToDevice(DeviceId deviceId, TrafficMatch trafficMatch) {
+        if (log.isDebugEnabled()) {
+            log.debug("Installing traffic match {} associated to policy {}",
+                    trafficMatch.trafficMatchId(), trafficMatch.policyId());
+        }
+        // Updates the store and then send the versatile fwd objective to the pipeliner
+        TrafficMatchKey trafficMatchKey = new TrafficMatchKey(deviceId, trafficMatch.trafficMatchId());
+        Operation trafficOperation = Operation.builder()
+                .isInstall(true)
+                .trafficMatch(trafficMatch)
+                .build();
+        operations.put(trafficMatchKey.toString(), trafficOperation);
+        // For the DROP policy we need to set an ACL drop in the fwd objective. The other
+        // policies require to retrieve the next Id and sets the next step.
+        PolicyKey policyKey = new PolicyKey(deviceId, trafficMatch.policyId());
+        Operation policyOperation = Versioned.valueOrNull(operations.get(policyKey.toString()));
+        if (policyOperation == null || !policyOperation.isDone() ||
+                !policyOperation.isInstall() || policyOperation.policy().isEmpty()) {
+            log.info("Deferring traffic match {} installation on device {}. Policy {} not yet installed",
+                    trafficMatch.trafficMatchId(), deviceId, trafficMatch.policyId());
+            return;
+        }
+        Policy policy = policyOperation.policy().get();
+        ForwardingObjective.Builder builder = trafficMatchFwdObjective(trafficMatch);
+        // TODO we can try to reuse some code: context and completable future logic
+        if (policy.policyType() == PolicyType.DROP) {
+            // Firstly builds the fwd objective with the wipeDeferred action. Once, the fwd
+            // objective has completed its execution, we update the policiesOps map
+            TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder()
+                    .wipeDeferred()
+                    .build();
+            builder.withTreatment(dropTreatment);
+            CompletableFuture<Objective> future = new CompletableFuture<>();
+            if (log.isDebugEnabled()) {
+                log.debug("Installing ACL drop forwarding objectives for dev: {}", deviceId);
+            }
+            ObjectiveContext context = new DefaultObjectiveContext(
+                    (objective) -> {
+                        if (log.isDebugEnabled()) {
+                            log.debug("ACL drop rule for policy {} installed", trafficMatch.policyId());
+                        }
+                        future.complete(objective);
+                    },
+                    (objective, error) -> {
+                        log.warn("Failed to install ACL drop rule for policy {}: {}", trafficMatch.policyId(), error);
+                        future.complete(null);
+                    });
+            // Context is not serializable
+            ForwardingObjective serializableObjective = builder.add();
+            flowObjectiveService.forward(deviceId, builder.add(context));
+            future.whenComplete((objective, ex) -> {
+                if (ex != null) {
+                    log.error("Exception installing ACL drop rule", ex);
+                } else if (objective != null) {
+                    operations.computeIfPresent(trafficMatchKey.toString(), (k, v) -> {
+                        if (!v.isDone() && v.isInstall())  {
+                            v.isDone(true);
+                            v.objectiveOperation(serializableObjective);
+                        }
+                        return v;
+                    });
+                }
+            });
+        } else {
+            log.warn("Policy {} type {} not yet supported", policy.policyId(), policy.policyType());
+        }
+    }
+
+    // Updates traffic match status if all the pending ops are done
+    private void updateTrafficMatch(TrafficMatch trafficMatch, boolean install) {
+        if (!isLeader(trafficMatch.policyId())) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", trafficMatch.policyId());
+            }
+            return;
+        }
+        workers.execute(() -> updateTrafficMatchInternal(trafficMatch.trafficMatchId(), install),
+                trafficMatch.policyId().hashCode());
+    }
+
+    private void updateTrafficMatchInternal(TrafficMatchId trafficMatchId, boolean install) {
+        // If there are no more pending ops we are ready to go; potentially we can check
+        // if the id is contained. Updates traffic matches only if they are still present
+        Optional<Map.Entry<String, Versioned<Operation>>> notYetDone = operations.entrySet().stream()
+                .filter(entry -> entry.getValue().value().trafficMatch().isPresent())
+                .filter(entry -> TrafficMatchKey.fromString(entry.getKey()).trafficMatchId().equals(trafficMatchId))
+                .filter(entry -> !entry.getValue().value().isDone() && entry.getValue().value().isInstall() == install)
+                .findFirst();
+        if (notYetDone.isEmpty()) {
+            trafficMatches.computeIfPresent(trafficMatchId, (k, v) -> {
+                if (v.trafficMatchState() == TrafficMatchState.PENDING_ADD && install)  {
+                    if (log.isDebugEnabled()) {
+                        log.debug("Traffic match {} is ready", trafficMatchId);
+                    }
+                    v.trafficMatchState(TrafficMatchState.ADDED);
+                } else if (v.trafficMatchState() == TrafficMatchState.PENDING_REMOVE && !install) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("Traffic match {} is removed", trafficMatchId);
+                    }
+                    v = null;
+                }
+                return v;
+            });
+        }
+    }
+
+    // Look for any pending traffic match waiting for the policy
+    private void updatePendingTrafficMatches(PolicyId policyId) {
+        Set<TrafficMatchRequest> pendingTrafficMatches = trafficMatches.stream()
+                .filter(trafficMatchEntry -> trafficMatchEntry.getValue().value().policyId().equals(policyId) &&
+                        trafficMatchEntry.getValue().value().trafficMatchState() == TrafficMatchState.PENDING_ADD)
+                .map(trafficMatchEntry -> trafficMatchEntry.getValue().value())
+                .collect(Collectors.toSet());
+        for (TrafficMatchRequest trafficMatch : pendingTrafficMatches) {
+            sendTrafficMatch(trafficMatch.trafficMatch(), true);
+        }
+    }
+
+    // Traffic match removal in a device
+    private void removeTrafficMatchInDevice(DeviceId deviceId, TrafficMatch trafficMatch) {
+        if (log.isDebugEnabled()) {
+            log.debug("Removing traffic match {} associated to policy {}",
+                    trafficMatch.trafficMatchId(), trafficMatch.policyId());
+        }
+        TrafficMatchKey trafficMatchKey = new TrafficMatchKey(deviceId, trafficMatch.trafficMatchId());
+        Operation operation = Versioned.valueOrNull(operations.get(trafficMatchKey.toString()));
+        if (operation == null || operation.objectiveOperation() == null) {
+            log.warn("There are no ops associated with {}", trafficMatchKey);
+            operation = Operation.builder()
+                    .isDone(true)
+                    .isInstall(false)
+                    .trafficMatch(trafficMatch)
+                    .build();
+            operations.put(trafficMatchKey.toString(), operation);
+        } else {
+            ForwardingObjective oldObj = (ForwardingObjective) operation.objectiveOperation();
+            operation = Operation.builder(operation)
+                    .isDone(false)
+                    .isInstall(false)
+                    .build();
+            operations.put(trafficMatchKey.toString(), operation);
+            ForwardingObjective.Builder builder = DefaultForwardingObjective.builder(oldObj);
+            CompletableFuture<Objective> future = new CompletableFuture<>();
+            if (log.isDebugEnabled()) {
+                log.debug("Removing ACL drop forwarding objectives for dev: {}", deviceId);
+            }
+            ObjectiveContext context = new DefaultObjectiveContext(
+                    (objective) -> {
+                        if (log.isDebugEnabled()) {
+                            log.debug("ACL drop rule for policy {} removed", trafficMatch.policyId());
+                        }
+                        future.complete(objective);
+                    },
+                    (objective, error) -> {
+                        log.warn("Failed to remove ACL drop rule for policy {}: {}", trafficMatch.policyId(), error);
+                        future.complete(null);
+                    });
+            ForwardingObjective serializableObjective = builder.remove();
+            flowObjectiveService.forward(deviceId, builder.remove(context));
+            future.whenComplete((objective, ex) -> {
+                if (ex != null) {
+                    log.error("Exception removing ACL drop rule", ex);
+                } else if (objective != null) {
+                    operations.computeIfPresent(trafficMatchKey.toString(), (k, v) -> {
+                        if (!v.isDone() && !v.isInstall())  {
+                            v.isDone(true);
+                            v.objectiveOperation(serializableObjective);
+                        }
+                        return v;
+                    });
+                }
+            });
+        }
+    }
+
+    // Update any depending traffic match on the policy. It is used when a policy
+    // has been removed but there are still traffic matches depending on it
+    private void updateDependingTrafficMatches(PolicyId policyId) {
+        if (!isLeader(policyId)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", policyId);
+            }
+            return;
+        }
+        workers.execute(() -> updateDependingTrafficMatchesInternal(policyId), policyId.hashCode());
+    }
+
+    private void updateDependingTrafficMatchesInternal(PolicyId policyId) {
+        Set<TrafficMatchRequest> pendingTrafficMatches = trafficMatches.stream()
+                .filter(trafficMatchEntry -> trafficMatchEntry.getValue().value().policyId().equals(policyId) &&
+                        trafficMatchEntry.getValue().value().trafficMatchState() == TrafficMatchState.ADDED)
+                .map(trafficMatchEntry -> trafficMatchEntry.getValue().value())
+                .collect(Collectors.toSet());
+        for (TrafficMatchRequest trafficMatchRequest : pendingTrafficMatches) {
+            trafficMatches.computeIfPresent(trafficMatchRequest.trafficMatchId(), (k, v) -> {
+                if (v.trafficMatchState() == TrafficMatchState.ADDED) {
+                    v.trafficMatchState(TrafficMatchState.PENDING_REMOVE);
+                }
+                return v;
+            });
+        }
+    }
+
+    // Utility that removes operations related to a policy or to a traffic match.
+    private void removeOperations(PolicyId policyId, Optional<TrafficMatchId> trafficMatchId) {
+        if (!isLeader(policyId)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Instance is not leader for policy {}", policyId);
+            }
+            return;
+        }
+        List<DeviceId> edgeDeviceIds = srService.getEdgeDeviceIds();
+        for (DeviceId deviceId : edgeDeviceIds) {
+            workers.execute(() -> {
+                String key;
+                if (trafficMatchId.isPresent()) {
+                    key = new TrafficMatchKey(deviceId, trafficMatchId.get()).toString();
+                } else {
+                    key = new PolicyKey(deviceId, policyId).toString();
+                }
+                operations.remove(key);
+            }, deviceId.hashCode());
+        }
+    }
+
+    private ForwardingObjective.Builder trafficMatchFwdObjective(TrafficMatch trafficMatch) {
+        return DefaultForwardingObjective.builder()
+                .withPriority(PolicyService.TRAFFIC_MATCH_PRIORITY)
+                .withSelector(trafficMatch.trafficSelector())
+                .fromApp(appId)
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .makePermanent();
+    }
+
+    // Each map has an event listener enabling the events distribution across the cluster
+    private class InternalPolMapEventListener implements MapEventListener<PolicyId, PolicyRequest> {
+        @Override
+        public void event(MapEvent<PolicyId, PolicyRequest> event) {
+            Versioned<PolicyRequest> value = event.type() == MapEvent.Type.REMOVE ?
+                    event.oldValue() : event.newValue();
+            PolicyRequest policyRequest = value.value();
+            Policy policy = policyRequest.policy();
+            switch (event.type()) {
+                case INSERT:
+                case UPDATE:
+                    switch (policyRequest.policyState()) {
+                        case PENDING_ADD:
+                            sendPolicy(policy, true);
+                            break;
+                        case PENDING_REMOVE:
+                            sendPolicy(policy, false);
+                            break;
+                        case ADDED:
+                            break;
+                        default:
+                            log.warn("Unknown policy state type {}", policyRequest.policyState());
+                    }
+                    break;
+                case REMOVE:
+                    removeOperations(policy.policyId(), Optional.empty());
+                    updateDependingTrafficMatches(policy.policyId());
+                    break;
+                default:
+                    log.warn("Unknown event type {}", event.type());
+
+            }
+        }
+    }
+
+    private class InternalTMatchMapEventListener implements MapEventListener<TrafficMatchId, TrafficMatchRequest> {
+        @Override
+        public void event(MapEvent<TrafficMatchId, TrafficMatchRequest> event) {
+            Versioned<TrafficMatchRequest> value = event.type() == MapEvent.Type.REMOVE ?
+                    event.oldValue() : event.newValue();
+            TrafficMatchRequest trafficMatchRequest = value.value();
+            TrafficMatch trafficMatch = trafficMatchRequest.trafficMatch();
+            switch (event.type()) {
+                case INSERT:
+                case UPDATE:
+                    switch (trafficMatchRequest.trafficMatchState()) {
+                        case PENDING_ADD:
+                            sendTrafficMatch(trafficMatch, true);
+                            break;
+                        case PENDING_REMOVE:
+                            sendTrafficMatch(trafficMatch, false);
+                            break;
+                        case ADDED:
+                            break;
+                        default:
+                            log.warn("Unknown traffic match state type {}", trafficMatchRequest.trafficMatchState());
+                    }
+                    break;
+                case REMOVE:
+                    removeOperations(trafficMatch.policyId(), Optional.of(trafficMatch.trafficMatchId()));
+                    break;
+                default:
+                    log.warn("Unknown event type {}", event.type());
+            }
+        }
+    }
+
+    private class InternalOpsMapEventListener implements MapEventListener<String, Operation> {
+        @Override
+        public void event(MapEvent<String, Operation> event) {
+            String key = event.key();
+            Versioned<Operation> value = event.type() == MapEvent.Type.REMOVE ?
+                    event.oldValue() : event.newValue();
+            Operation operation = value.value();
+            switch (event.type()) {
+                case INSERT:
+                case UPDATE:
+                    if (operation.isDone()) {
+                        if (operation.policy().isPresent()) {
+                            PolicyKey policyKey = PolicyKey.fromString(key);
+                            updatePolicy(policyKey.policyId(), operation.isInstall());
+                        } else if (operation.trafficMatch().isPresent()) {
+                            updateTrafficMatch(operation.trafficMatch().get(), operation.isInstall());
+                        } else {
+                            log.warn("Unknown pending operation");
+                        }
+                    }
+                    break;
+                case REMOVE:
+                    break;
+                default:
+                    log.warn("Unknown event type {}", event.type());
+            }
+        }
+    }
+
+    // Using the work partition service defines who is in charge of a given policy.
+    private boolean isLeader(PolicyId policyId) {
+        final NodeId currentNodeId = clusterService.getLocalNode().id();
+        final NodeId leader = workPartitionService.getLeader(policyId, this::hasher);
+        if (leader == null) {
+            log.error("Fail to elect a leader for {}.", policyId);
+            return false;
+        }
+        policyLeaderCache.put(policyId, leader);
+        return currentNodeId.equals(leader);
+    }
+
+    private Long hasher(PolicyId policyId) {
+        return HASH_FN.newHasher()
+                .putUnencodedChars(policyId.toString())
+                .hash()
+                .asLong();
+    }
+
+    // Check periodically for any issue and try to resolve automatically if possible
+    private final class PolicyChecker implements Runnable {
+        @Override
+        public void run() {
+        }
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyRequest.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyRequest.java
new file mode 100644
index 0000000..451c1a9
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyRequest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import org.onosproject.segmentrouting.policy.api.Policy;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyState;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Policy request tracked by the system.
+ */
+final class PolicyRequest {
+    // Stores need to track policy info and the state
+    private final Policy policy;
+    private PolicyState policyState;
+
+    /**
+     * Creates a policy request in pending add.
+     *
+     * @param pol the policy
+     */
+    public PolicyRequest(Policy pol) {
+        policy = pol;
+        policyState = PolicyState.PENDING_ADD;
+    }
+
+    /**
+     * Returns the current state of the request.
+     *
+     * @return the policy state
+     */
+    public PolicyState policyState() {
+        return policyState;
+    }
+
+    /**
+     * Returns the policy id.
+     *
+     * @return the policy id
+     */
+    public PolicyId policyId() {
+        return policy.policyId();
+    }
+
+    /**
+     * Returns the policy type.
+     *
+     * @return the type of a policy
+     */
+    public Policy.PolicyType policyType() {
+        return policy.policyType();
+    }
+
+    /**
+     * To update the policy state.
+     *
+     * @param policystate the new state.
+     */
+    public void policyState(PolicyState policystate) {
+        policyState = policystate;
+    }
+
+    /**
+     * Returns the policy associated to this request.
+     *
+     * @return the policy
+     */
+    public Policy policy() {
+        return policy;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PolicyRequest)) {
+            return false;
+        }
+        final PolicyRequest other = (PolicyRequest) obj;
+        return Objects.equals(this.policy, other.policy);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(policy);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("policyState", policyState)
+                .add("policy", policy)
+                .toString();
+    }
+
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchKey.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchKey.java
new file mode 100644
index 0000000..68181a6
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchKey.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * Traffic match key used by the store.
+ */
+public class TrafficMatchKey {
+    private DeviceId deviceId;
+    private TrafficMatchId trafficMatchId;
+
+    /**
+     * Constructs new traffic match key with given device id and traffic match id.
+     *
+     * @param deviceId device id
+     * @param trafficMatchId traffic match id
+     */
+    public TrafficMatchKey(DeviceId deviceId, TrafficMatchId trafficMatchId) {
+        this.deviceId = deviceId;
+        this.trafficMatchId = trafficMatchId;
+    }
+
+    /**
+     * Gets device id.
+     *
+     * @return device id of the policy key
+     */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    /**
+     * Gets traffic match id.
+     *
+     * @return the id of the traffic match
+     */
+    public TrafficMatchId trafficMatchId() {
+        return trafficMatchId;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof TrafficMatchKey)) {
+            return false;
+        }
+        final TrafficMatchKey other = (TrafficMatchKey) obj;
+        return Objects.equals(this.deviceId, other.deviceId) &&
+                Objects.equals(this.trafficMatchId, other.trafficMatchId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(deviceId, trafficMatchId);
+    }
+
+    /**
+     * Parses from a string the police key.
+     *
+     * @param str the string to parse
+     * @return the policy key if present in the str, null otherwise
+     */
+    public static TrafficMatchKey fromString(String str) {
+        TrafficMatchKey policyKey = null;
+        if (str != null && str.contains(PolicyManager.KEY_SEPARATOR)) {
+            StringTokenizer tokenizer = new StringTokenizer(str, PolicyManager.KEY_SEPARATOR);
+            if (tokenizer.countTokens() == 2) {
+                policyKey = new TrafficMatchKey(DeviceId.deviceId(tokenizer.nextToken()),
+                        TrafficMatchId.of(tokenizer.nextToken()));
+            }
+        }
+        return policyKey;
+    }
+
+    @Override
+    public String toString() {
+        return deviceId.toString() + PolicyManager.KEY_SEPARATOR + trafficMatchId.toString();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchRequest.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchRequest.java
new file mode 100644
index 0000000..6e01e01
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/TrafficMatchRequest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.impl;
+
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchState;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Representation of a traffic match.
+ */
+public final class TrafficMatchRequest {
+    // Stores need to track the traffic match info and the state
+    private final TrafficMatch trafficMatch;
+    private TrafficMatchState trafficMatchState;
+
+    /**
+     * Builds a traffic match request in pending add.
+     *
+     * @param tMatch the traffic match
+     */
+    public TrafficMatchRequest(TrafficMatch tMatch) {
+        trafficMatch = tMatch;
+        trafficMatchState = TrafficMatchState.PENDING_ADD;
+    }
+
+    /**
+     * To update the traffic match state.
+     *
+     * @param trafficMatchState the new state
+     */
+    public void trafficMatchState(TrafficMatchState trafficMatchState) {
+        this.trafficMatchState = trafficMatchState;
+    }
+
+    /**
+     * Returns the traffic match state.
+     *
+     * @return the state of the traffic match
+     */
+    public TrafficMatchState trafficMatchState() {
+        return trafficMatchState;
+    }
+
+    /**
+     * Returns the traffic match id.
+     *
+     * @return the id of the traffic match
+     */
+    public TrafficMatchId trafficMatchId() {
+        return trafficMatch.trafficMatchId();
+    }
+
+    /**
+     * Returns the id of the policy associated with.
+     *
+     * @return the policy id
+     */
+    public PolicyId policyId() {
+        return trafficMatch.policyId();
+    }
+
+    /**
+     * Returns the traffic selector associated with.
+     *
+     * @return the traffic selector
+     */
+    public TrafficSelector trafficSelector() {
+        return trafficMatch.trafficSelector();
+    }
+
+    /**
+     * Returns the traffic match associated with.
+     *
+     * @return the traffic match
+     */
+    public TrafficMatch trafficMatch() {
+        return trafficMatch;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(trafficMatch);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TrafficMatchRequest) {
+            final TrafficMatchRequest other = (TrafficMatchRequest) obj;
+            return Objects.equals(trafficMatch, other.trafficMatch);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("trafficMatch", trafficMatch)
+                .add("trafficMatchState", trafficMatchState)
+                .toString();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/package-info.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/package-info.java
new file mode 100644
index 0000000..f5199ca
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Policy implementation.
+ */
+package org.onosproject.segmentrouting.policy.impl;
\ No newline at end of file