[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/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
index 2d0bbd2..bb86aac 100644
--- a/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
@@ -101,6 +101,13 @@
     boolean isEdgeDevice(DeviceId deviceId) throws DeviceConfigNotFoundException;
 
     /**
+     * Returns a list of edge devices.
+     *
+     * @return list of the edge device ids
+     */
+    List<DeviceId> getEdgeDeviceIds();
+
+    /**
      * Returns all segment IDs to be considered in building auto
      *
      * created groups.
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
index 3251e7a..04d63a2 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -394,6 +394,14 @@
     }
 
     @Override
+    public List<DeviceId> getEdgeDeviceIds() {
+        return deviceConfigMap.values().stream()
+                .filter(deviceInfo -> deviceInfo.isEdge)
+                .map(deviceInfo -> deviceInfo.deviceId)
+                .collect(Collectors.toList());
+    }
+
+    @Override
     public List<Integer> getAllDeviceSegmentIds() {
         return allSegmentIds;
     }
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 9e98218..f65d4ea 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -1199,6 +1199,11 @@
                 .collect(Collectors.toSet());
     }
 
+    @Override
+    public List<DeviceId> getEdgeDeviceIds() {
+        return deviceConfiguration.getEdgeDeviceIds();
+    }
+
     /**
      * Returns locations of given resolved route.
      *
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 3bfd88b..95b5be5 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -436,4 +436,11 @@
     default Set<PortNumber> getEdgePorts(DeviceId deviceId) {
         throw new NotImplementedException("getEdgePorts not implemented");
     }
+
+    /**
+     * Returns a list of edge devices.
+     *
+     * @return list of the edge device ids
+     */
+    List<DeviceId> getEdgeDeviceIds();
 }
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/DropPolicyAddCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/DropPolicyAddCommand.java
new file mode 100644
index 0000000..9c3cb51
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/DropPolicyAddCommand.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015-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.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.policy.api.DropPolicy;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+
+/**
+ * Command to add a new drop policy.
+ */
+@Service
+@Command(scope = "onos", name = "sr-drop-policy-add",
+        description = "Create a new drop policy")
+public class DropPolicyAddCommand extends AbstractShellCommand {
+
+    @Override
+    protected void doExecute() {
+        PolicyService policyService = AbstractShellCommand.get(PolicyService.class);
+        PolicyId policyId = policyService.addOrUpdatePolicy(new DropPolicy());
+        print("Policy %s has been submitted", policyId);
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
deleted file mode 100644
index 1f75317..0000000
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2015-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.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.PolicyHandler;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
-
-/**
- * Command to add a new policy.
- */
-@Service
-@Command(scope = "onos", name = "sr-policy-add",
-        description = "Create a new policy")
-public class PolicyAddCommand extends AbstractShellCommand {
-
-    // TODO: Need to support skipping some parameters
-
-    @Argument(index = 0, name = "ID",
-            description = "policy ID",
-            required = true, multiValued = false)
-    String policyId;
-
-    @Argument(index = 1, name = "priority",
-            description = "priority",
-            required = true, multiValued = false)
-    int priority;
-
-    @Argument(index = 2, name = "src_IP",
-            description = "src IP",
-            required = false, multiValued = false)
-    String srcIp;
-
-    @Argument(index = 3, name = "src_port",
-            description = "src port",
-            required = false, multiValued = false)
-    short srcPort;
-
-    @Argument(index = 4, name = "dst_IP",
-            description = "dst IP",
-            required = false, multiValued = false)
-    String dstIp;
-
-    @Argument(index = 5, name = "dst_port",
-            description = "dst port",
-            required = false, multiValued = false)
-    short dstPort;
-
-    @Argument(index = 6, name = "proto",
-            description = "IP protocol",
-            required = false, multiValued = false)
-    String proto;
-
-    @Argument(index = 7, name = "policy_type",
-            description = "policy type",
-            required = true, multiValued = false)
-    String policyType;
-
-    @Argument(index = 8, name = "tunnel_ID",
-            description = "tunnel ID",
-            required = false, multiValued = false)
-    String tunnelId;
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
-        tpb.setPriority(priority);
-        tpb.setType(Policy.Type.valueOf(policyType));
-
-        if (srcIp != null) {
-            tpb.setSrcIp(srcIp);
-        }
-        if (dstIp != null) {
-            tpb.setDstIp(dstIp);
-        }
-        if (srcPort != 0) {
-            tpb.setSrcPort(srcPort);
-        }
-        if (dstPort != 0) {
-            tpb.setDstPort(dstPort);
-        }
-        if (!"ip".equals(proto)) {
-            tpb.setIpProto(proto);
-        }
-        if (Policy.Type.valueOf(policyType) == Policy.Type.TUNNEL_FLOW) {
-            if (tunnelId == null) {
-                error("tunnel ID must be specified for TUNNEL_FLOW policy");
-                return;
-            }
-            tpb.setTunnelId(tunnelId);
-        }
-        PolicyHandler.Result result = srService.createPolicy(tpb.build());
-
-        switch (result) {
-            case POLICY_EXISTS:
-                error("the same policy exists");
-                break;
-            case ID_EXISTS:
-                error("the same policy ID exists");
-                break;
-            case TUNNEL_NOT_FOUND:
-                error("the tunnel is not found");
-                break;
-            case UNSUPPORTED_TYPE:
-                error("the policy type specified is not supported");
-                break;
-            default:
-                break;
-        }
-
-    }
-}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
index 3520fba..1c61912 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
@@ -16,11 +16,16 @@
 package org.onosproject.segmentrouting.cli;
 
 import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.glassfish.jersey.internal.guava.Sets;
+
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
+import org.onosproject.segmentrouting.policy.api.Policy;
+import org.onosproject.segmentrouting.policy.api.PolicyData;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+
+import java.util.Set;
 
 /**
  * Command to show the list of policies.
@@ -30,24 +35,37 @@
         description = "Lists all policies")
 public class PolicyListCommand extends AbstractShellCommand {
 
-    private static final String FORMAT_MAPPING_TUNNEL =
-            "  id=%s, type=%s,  prio=%d, src=%s, port=%d, dst=%s, port=%d, proto=%s, tunnel=%s";
+    private static final String FORMAT_MAPPING_POLICY =
+            "  id=%s, state=%s, type=%s";
+    private static final String FORMAT_MAPPING_OPERATION =
+            "    op=%s";
+
+    @Option(name = "-filt", aliases = "--filter",
+            description = "Filter based on policy type",
+            valueToShowInHelp = "DROP",
+            multiValued = true)
+    String[] filters = null;
 
     @Override
     protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        srService.getPolicies().forEach(policy -> printPolicy(policy));
+        PolicyService policyService =
+                AbstractShellCommand.get(PolicyService.class);
+        policyService.policies(policyTypes()).forEach(this::printPolicy);
     }
 
-    private void printPolicy(Policy policy) {
-        if (policy.type() == Policy.Type.TUNNEL_FLOW) {
-            print(FORMAT_MAPPING_TUNNEL, policy.id(), policy.type(), policy.priority(),
-                    policy.srcIp(), policy.srcPort(), policy.dstIp(), policy.dstPort(),
-                    (policy.ipProto() == null) ? "" : policy.ipProto(),
-                    ((TunnelPolicy) policy).tunnelId());
+    private Set<Policy.PolicyType> policyTypes() {
+        Set<Policy.PolicyType> policyTypes = Sets.newHashSet();
+        if (filters != null) {
+            for (String filter : filters) {
+                policyTypes.add(Policy.PolicyType.valueOf(filter));
+            }
         }
+        return policyTypes;
+    }
+
+    private void printPolicy(PolicyData policyData) {
+        print(FORMAT_MAPPING_POLICY, policyData.policy().policyId(), policyData.policyState(),
+                policyData.policy().policyType());
+        policyData.operations().forEach(operation -> print(FORMAT_MAPPING_OPERATION, operation));
     }
 }
\ No newline at end of file
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
index 575d35f..ff35423 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
@@ -15,14 +15,13 @@
  */
 package org.onosproject.segmentrouting.cli;
 
-
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
+
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.PolicyHandler;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
 
 /**
  * Command to remove a policy.
@@ -32,21 +31,18 @@
         description = "Remove a policy")
 public class PolicyRemoveCommand extends AbstractShellCommand {
 
-    @Argument(index = 0, name = "policy ID",
-            description = "policy ID",
+    @Argument(index = 0, name = "policyId",
+            description = "policy id",
             required = true, multiValued = false)
     String policyId;
 
     @Override
     protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
-        PolicyHandler.Result result = srService.removePolicy(tpb.build());
-        if (result == PolicyHandler.Result.POLICY_NOT_FOUND) {
-            print("ERROR: the policy is not found");
+        PolicyService policyService = AbstractShellCommand.get(PolicyService.class);
+        if (policyService.removePolicy(PolicyId.of(policyId))) {
+            print("Removing policy %s", policyId);
+        } else {
+            print("Unable to remove policy %s", policyId);
         }
     }
 }
\ No newline at end of file
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchAddCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchAddCommand.java
new file mode 100644
index 0000000..d8b7a0a
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchAddCommand.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015-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.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.IpProtocol;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+
+/**
+ * Command to add a traffic match.
+ */
+@Service
+@Command(scope = "onos", name = "sr-tmatch-add",
+        description = "Create a new traffic match")
+public class TrafficMatchAddCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "policyId",
+            description = "policy id",
+            required = true, multiValued = false)
+    String policyId;
+
+    @Argument(index = 1, name = "srcIp",
+            description = "src IP",
+            required = false, multiValued = false)
+    String srcIp;
+
+    @Argument(index = 2, name = "srcPort",
+            description = "src port",
+            required = false, multiValued = false)
+    short srcPort;
+
+    @Argument(index = 3, name = "dstIp",
+            description = "dst IP",
+            required = false, multiValued = false)
+    String dstIp;
+
+    @Argument(index = 4, name = "dstPort",
+            description = "dst port",
+            required = false, multiValued = false)
+    short dstPort;
+
+    @Argument(index = 5, name = "proto",
+            description = "IP protocol",
+            required = false, multiValued = false)
+    String proto;
+
+    @Argument(index = 6, name = "srcMac",
+            description = "src MAC",
+            required = false, multiValued = false)
+    String srcMac;
+
+    @Argument(index = 7, name = "dstMac",
+            description = "dst MAC",
+            required = false, multiValued = false)
+    String dstMac;
+
+    @Argument(index = 8, name = "vlanId",
+            description = "VLAN id",
+            required = false, multiValued = false)
+    short vlanId = -1;
+
+    @Override
+    protected void doExecute() {
+        TrafficSelector trafficSelector = parseArguments();
+        if (trafficSelector.equals(DefaultTrafficSelector.emptySelector())) {
+            print("Empty traffic selector is not allowed");
+            return;
+        }
+
+        PolicyService policyService = AbstractShellCommand.get(PolicyService.class);
+        TrafficMatchId trafficMatchId = policyService.addOrUpdateTrafficMatch(
+                new TrafficMatch(trafficSelector, PolicyId.of(policyId)));
+        print("Traffic match %s has been submitted", trafficMatchId);
+    }
+
+    private TrafficSelector parseArguments() {
+        TrafficSelector.Builder trafficSelectorBuilder = DefaultTrafficSelector.builder();
+        if (srcIp != null) {
+            trafficSelectorBuilder.matchIPSrc(IpPrefix.valueOf(srcIp));
+        }
+        if (dstIp != null) {
+            trafficSelectorBuilder.matchIPDst(IpPrefix.valueOf(dstIp));
+        }
+        byte ipProtocol = 0;
+        if (proto != null) {
+            ipProtocol = (byte) (0xFF & IpProtocol.parseFromString(proto));
+            trafficSelectorBuilder.matchIPProtocol(ipProtocol);
+        }
+        if (srcPort != 0) {
+            if (ipProtocol == IPv4.PROTOCOL_TCP) {
+                trafficSelectorBuilder.matchTcpSrc(TpPort.tpPort(srcPort));
+            } else if (ipProtocol == IPv4.PROTOCOL_UDP) {
+                trafficSelectorBuilder.matchUdpSrc(TpPort.tpPort(srcPort));
+            }
+        }
+        if (dstPort != 0) {
+            if (ipProtocol == IPv4.PROTOCOL_TCP) {
+                trafficSelectorBuilder.matchTcpDst(TpPort.tpPort(dstPort));
+            } else if (ipProtocol == IPv4.PROTOCOL_UDP) {
+                trafficSelectorBuilder.matchUdpDst(TpPort.tpPort(dstPort));
+            }
+        }
+        if (srcMac != null) {
+            trafficSelectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
+        }
+        if (dstMac != null) {
+            trafficSelectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
+        }
+        if (vlanId != -1) {
+            trafficSelectorBuilder.matchVlanId(VlanId.vlanId(vlanId));
+        }
+        return trafficSelectorBuilder.build();
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchListCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchListCommand.java
new file mode 100644
index 0000000..c00d1fa
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchListCommand.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015-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.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchData;
+
+/**
+ * Command to show the list of traffic matches.
+ */
+@Service
+@Command(scope = "onos", name = "sr-tmatch-list",
+        description = "Lists all traffic matches")
+public class TrafficMatchListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT_MAPPING_TRAFFIC_MATCH =
+            "  id=%s, state=%s";
+    private static final String FORMAT_MAPPING_OPERATION =
+            "    op=%s";
+
+    @Override
+    protected void doExecute() {
+        PolicyService policyService = AbstractShellCommand.get(PolicyService.class);
+        policyService.trafficMatches().forEach(this::printTrafficMatch);
+    }
+
+    private void printTrafficMatch(TrafficMatchData trafficMatchData) {
+        print(FORMAT_MAPPING_TRAFFIC_MATCH, trafficMatchData.trafficMatch().trafficMatchId(),
+                trafficMatchData.trafficMatchState());
+        trafficMatchData.operations().forEach(operation -> print(FORMAT_MAPPING_OPERATION, operation));
+    }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchRemoveCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchRemoveCommand.java
new file mode 100644
index 0000000..763e9c8
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/cli/TrafficMatchRemoveCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015-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.cli;
+
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+
+/**
+ * Command to remove a traffic match.
+ */
+@Service
+@Command(scope = "onos", name = "sr-tmatch-remove",
+        description = "Remove a traffic match")
+public class TrafficMatchRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "trafficMatchId",
+            description = "traffic match id",
+            required = true, multiValued = false)
+    String trafficMatchId;
+
+    @Override
+    protected void doExecute() {
+        PolicyService policyService = AbstractShellCommand.get(PolicyService.class);
+        trafficMatchId = trafficMatchId.replace("\\", "");
+        if (policyService.removeTrafficMatch(TrafficMatchId.of(trafficMatchId))) {
+            print("Removing traffic match %s", trafficMatchId);
+        } else {
+            print("Unable to remove traffic match %s", trafficMatchId);
+        }
+    }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
deleted file mode 100644
index 0e487d0..0000000
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2015-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.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.DefaultTunnel;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-import org.onosproject.segmentrouting.TunnelHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-/**
- * Command to add a new tunnel.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-add",
-        description = "Create a new tunnel")
-public class TunnelAddCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "tunnel ID",
-            description = "tunnel ID",
-            required = true, multiValued = false)
-    String tunnelId;
-
-    @Argument(index = 1, name = "label path",
-            description = "label path",
-            required = true, multiValued = false)
-    String labels;
-
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        List<Integer> labelIds = new ArrayList<>();
-        StringTokenizer strToken = new StringTokenizer(labels, ",");
-        while (strToken.hasMoreTokens()) {
-            labelIds.add(Integer.valueOf(strToken.nextToken()));
-        }
-        Tunnel tunnel = new DefaultTunnel(tunnelId, labelIds);
-
-        TunnelHandler.Result result = srService.createTunnel(tunnel);
-        switch (result) {
-            case ID_EXISTS:
-                print("ERROR: the same tunnel ID exists");
-                break;
-            case TUNNEL_EXISTS:
-                print("ERROR: the same tunnel exists");
-                break;
-            case INTERNAL_ERROR:
-                print("ERROR: internal tunnel creation error");
-                break;
-            case WRONG_PATH:
-                print("ERROR: the tunnel path is wrong");
-                break;
-            default:
-                break;
-        }
-    }
-}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
deleted file mode 100644
index 6c5f33d..0000000
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015-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.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-
-/**
- * Command to show the list of tunnels.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-list",
-        description = "Lists all tunnels")
-public class TunnelListCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_MAPPING =
-            "  id=%s, path=%s";
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        srService.getTunnels().forEach(tunnel -> printTunnel(tunnel));
-    }
-
-    private void printTunnel(Tunnel tunnel) {
-        print(FORMAT_MAPPING, tunnel.id(), tunnel.labelIds());
-    }
-}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java b/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
deleted file mode 100644
index f5f0299..0000000
--- a/impl/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2015-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.cli;
-
-
-import com.google.common.collect.Lists;
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.DefaultTunnel;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-import org.onosproject.segmentrouting.TunnelHandler;
-
-/**
- * Command to remove a tunnel.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-remove",
-        description = "Remove a tunnel")
-public class TunnelRemoveCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "tunnel ID",
-            description = "tunnel ID",
-            required = true, multiValued = false)
-    String tunnelId;
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        Tunnel tunnel = new DefaultTunnel(tunnelId, Lists.newArrayList());
-        TunnelHandler.Result result = srService.removeTunnel(tunnel);
-        switch (result) {
-            case TUNNEL_IN_USE:
-                print("ERROR: the tunnel is still in use");
-                break;
-            case TUNNEL_NOT_FOUND:
-                print("ERROR: the tunnel is not found");
-                break;
-            default:
-                break;
-        }
-    }
-}
\ No newline at end of file
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