Added dump of table entry ids in bmv2 protocol

Change-Id: I54534cfb2c6188c922b36a2f8eb8e5c0851bc681
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
index 324e3e4..d34aa9d 100644
--- a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
@@ -19,6 +19,7 @@
 import org.onlab.util.ImmutableByteSequence;
 
 import java.util.Collection;
+import java.util.List;
 
 /**
  * RPC client to control a BMv2 device.
@@ -83,6 +84,24 @@
     String dumpTable(String tableName) throws Bmv2RuntimeException;
 
     /**
+     * Returns a list of ids for the entries installed in the given table.
+     *
+     * @param tableName string value of table name
+     * @return a list of entry ids
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    List<Long> getInstalledEntryIds(String tableName) throws Bmv2RuntimeException;
+
+    /**
+     * Removes all entries installed in the given table.
+     *
+     * @param tableName string value of table name
+     * @return the number of entries removed
+     * @throws Bmv2RuntimeException if any error occurs
+     */
+    int cleanupTable(String tableName) throws Bmv2RuntimeException;
+
+    /**
      * Requests the device to transmit a given byte sequence over the given port.
      *
      * @param portNumber a port number
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2TableDumpParser.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2TableDumpParser.java
new file mode 100644
index 0000000..0d413da
--- /dev/null
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2TableDumpParser.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2.ctl;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * String parser for the BMv2 table dump.
+ */
+public class Bmv2TableDumpParser {
+
+    /*
+    Example of BMv2 table dump:
+    0: 0000 000000000000 000000000000 0806 &&& 0000000000000000000000000000ffff => send_to_cpu -
+
+    For each entry, we want to match the id and all the rest.
+     */
+    private static final String ENTRY_PATTERN_STRING = "(\\d+):(.+)";
+    private static final Pattern ENTRY_PATTERN = Pattern.compile(ENTRY_PATTERN_STRING);
+
+    /**
+     * Returns a list of entry Ids for the given table dump.
+     *
+     * @param tableDump a string value
+     * @return a list of long values
+     * @throws Bmv2TableDumpParserException if dump can't be parsed
+     */
+    public List<Long> getEntryIds(String tableDump) throws Bmv2TableDumpParserException {
+        return parse(tableDump).stream().map(Pair::getKey).collect(Collectors.toList());
+    }
+
+    private List<Pair<Long, String>> parse(String tableDump) throws Bmv2TableDumpParserException {
+        checkNotNull(tableDump, "tableDump cannot be null");
+
+        List<Pair<Long, String>> results = Lists.newArrayList();
+
+        // TODO: consider caching parser results for speed.
+
+        Matcher matcher = ENTRY_PATTERN.matcher(tableDump);
+
+        while (matcher.find()) {
+            String entryString = matcher.group(1);
+            if (entryString == null) {
+                throw new Bmv2TableDumpParserException("Unable to parse entry for string: " + matcher.group());
+            }
+            Long entryId = -1L;
+            try {
+                entryId = Long.valueOf(entryString.trim());
+            } catch (NumberFormatException e) {
+                throw new Bmv2TableDumpParserException("Unable to parse entry id for string: " + matcher.group());
+            }
+            String allTheRest = matcher.group(2);
+            if (allTheRest == null) {
+                throw new Bmv2TableDumpParserException("Unable to parse entry for string: " + matcher.group());
+            }
+            results.add(Pair.of(entryId, allTheRest));
+        }
+
+        return results;
+    }
+
+    public class Bmv2TableDumpParserException extends Throwable {
+        public Bmv2TableDumpParserException(String msg) {
+            super(msg);
+        }
+    }
+}
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
index d866c55..66229b0 100644
--- a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
@@ -90,6 +90,9 @@
             .expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
             .removalListener(new ClientRemovalListener())
             .build(new ClientLoader());
+
+    private static final Bmv2TableDumpParser TABLE_DUMP_PARSER = new Bmv2TableDumpParser();
+
     private final Standard.Iface standardClient;
     private final SimpleSwitch.Iface simpleSwitchClient;
     private final TTransport transport;
@@ -391,6 +394,44 @@
     }
 
     @Override
+    public List<Long> getInstalledEntryIds(String tableName) throws Bmv2RuntimeException {
+
+        LOG.debug("Getting entry ids... > deviceId={}, tableName={}", deviceId, tableName);
+
+        try {
+            List<Long> entryIds = TABLE_DUMP_PARSER.getEntryIds(dumpTable(tableName));
+            LOG.debug("Entry ids retrieved! > deviceId={}, tableName={}, entryIdsCount={}",
+                      deviceId, tableName, entryIds.size());
+            return entryIds;
+        } catch (Bmv2TableDumpParser.Bmv2TableDumpParserException e) {
+            LOG.debug("Exception while retrieving entry ids: {} > deviceId={}, tableName={}",
+                      e, deviceId, tableName);
+            throw new Bmv2RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public int cleanupTable(String tableName) throws Bmv2RuntimeException {
+
+        LOG.debug("Starting table cleanup... > deviceId={}, tableName={}", deviceId, tableName);
+
+        List<Long> entryIds = getInstalledEntryIds(tableName);
+
+        int count = 0;
+        for (Long entryId : entryIds) {
+            try {
+                standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
+                count++;
+            } catch (TException e) {
+                LOG.warn("Exception while deleting entry: {} > deviceId={}, tableName={}, entryId={}",
+                         e.toString(), deviceId, tableName, entryId);
+            }
+        }
+
+        return count;
+    }
+
+    @Override
     public void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException {
 
         LOG.debug("Requesting packet transmission... > portNumber={}, packet={}", portNumber, packet);
diff --git a/protocols/bmv2/src/test/java/org/onosproject/bmv2/api/model/Bmv2TableDumpParserTest.java b/protocols/bmv2/src/test/java/org/onosproject/bmv2/api/model/Bmv2TableDumpParserTest.java
new file mode 100644
index 0000000..87ae453
--- /dev/null
+++ b/protocols/bmv2/src/test/java/org/onosproject/bmv2/api/model/Bmv2TableDumpParserTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2.api.model;
+
+import org.junit.Test;
+import org.onosproject.bmv2.ctl.Bmv2TableDumpParser;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+
+public class Bmv2TableDumpParserTest {
+
+    @Test
+    public void testParse() throws Exception, Bmv2TableDumpParser.Bmv2TableDumpParserException {
+
+        String text =
+                "0: 0000 000000000000 000000000000 &&& 0000000000000000000000000000 => send_to_cpu -\n" +
+                        "1: 0000 000000000000 000000000000 &&& 0000000000000000000000000000 => send_to_cpu -\n" +
+                        "2: 0000 000000000000 000000000000 &&& 0000000000000000000000000000 => send_to_cpu -\n" +
+                        "3: 0000 000000000000 000000000000 &&& 0000000000000000000000000000 => send_to_cpu -";
+
+        Bmv2TableDumpParser parser = new Bmv2TableDumpParser();
+
+        List<Long> result = parser.getEntryIds(text);
+
+        assertThat("invalid parsed values", result.get(0), is(equalTo(0L)));
+        assertThat("invalid parsed values", result.get(1), is(equalTo(1L)));
+        assertThat("invalid parsed values", result.get(2), is(equalTo(2L)));
+        assertThat("invalid parsed values", result.get(3), is(equalTo(3L)));
+    }
+}