mgmt: added FIB manager support for insert, delete, and remove-nexthop verbs

Refactor option decoding, verb authorization (placeholder),
and response sending into single location.
Added unit tests for ManagerBase.

refs: #1223

Change-Id: I731be586ee8f06defb00fcadc6010409560891a1
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 69be19f..3bcbb1a 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -11,12 +11,9 @@
 #include "mgmt/internal-face.hpp"
 #include "mgmt/app-face.hpp"
 
-
-
 #include <ndn-cpp-dev/management/fib-management-options.hpp>
 #include <ndn-cpp-dev/encoding/tlv.hpp>
 
-#include <ndn-cpp-dev/management/fib-management-options.hpp>
 
 namespace nfd {
 
@@ -35,33 +32,32 @@
 
 const FibManager::VerbAndProcessor FibManager::FIB_MANAGER_COMMAND_VERBS[] =
   {
-    // Unsupported
+    VerbAndProcessor(
+                     "insert",
+                     &FibManager::insertEntry
+                     ),
 
-    // VerbAndProcessor(
-    //                  "insert",
-    //                  &FibManager::fibInsert
-    //                  ),
-
-    // VerbAndProcessor(
-    //                  "delete",
-    //                  &FibManager::fibDelete
-    //                  ),
+    VerbAndProcessor(
+                     "delete",
+                     &FibManager::deleteEntry
+                     ),
 
     VerbAndProcessor(
                      "add-nexthop",
-                     &FibManager::fibAddNextHop
+                     &FibManager::addNextHop
+                     ),
+
+
+
+    VerbAndProcessor(
+                     "remove-nexthop",
+                     &FibManager::removeNextHop
                      ),
 
     // Unsupported
-
-    // VerbAndProcessor(
-    //                  "remove-nexthop",
-    //                  &FibManager::fibRemoveNextHop
-    //                  ),
-
     // VerbAndProcessor(
     //                  "strategy",
-    //                  &FibManager::fibStrategy
+    //                  &FibManager::strategy
     //                  )
 
   };
@@ -86,12 +82,10 @@
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
 
-  /// \todo Separate out response status codes 400 and 401
-
   if (FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
       commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS)
     {
-      NFD_LOG_INFO("Unsigned command: " << command);
+      NFD_LOG_INFO("command result: unsigned verb: " << command);
       sendResponse(command, 401, "Signature required");
 
       return;
@@ -99,45 +93,51 @@
   else if (commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS ||
       !FIB_MANAGER_COMMAND_PREFIX.isPrefixOf(command))
     {
-      NFD_LOG_INFO("Malformed command: " << command);
+      NFD_LOG_INFO("command result: malformed");
       sendResponse(command, 400, "Malformed command");
       return;
     }
 
-
   const Name::Component& verb = command.get(FIB_MANAGER_COMMAND_PREFIX.size());
 
   VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (verb);
   if (verbProcessor != m_verbDispatch.end())
     {
-      NFD_LOG_INFO("Processing command verb: " << verb);
-      (verbProcessor->second)(this, request);
+      ndn::FibManagementOptions options;
+      if (!extractOptions(request, options))
+        {
+          sendResponse(command, 400, "Malformed command");
+          return;
+        }
+
+      /// \todo authorize command
+      if (false)
+        {
+          NFD_LOG_INFO("command result: unauthorized verb: " << command);
+          sendResponse(request.getName(), 403, "Unauthorized command");
+          return;
+        }
+
+      NFD_LOG_INFO("command result: processing verb: " << verb);
+
+      ndn::ControlResponse response;
+      (verbProcessor->second)(this, options, response);
+
+      sendResponse(command, response);
     }
   else
     {
-      NFD_LOG_INFO("Unsupported command verb: " << verb);
+      NFD_LOG_INFO("command result: unsupported verb: " << verb);
       sendResponse(request.getName(), 501, "Unsupported command");
     }
 
 }
 
-void
-FibManager::fibInsert(const Interest& request)
-{
-
-}
-
-void
-FibManager::fibDelete(const Interest& request)
-{
-
-}
-
-void
-FibManager::fibAddNextHop(const Interest& request)
+bool
+FibManager::extractOptions(const Interest& request,
+                           ndn::FibManagementOptions& extractedOptions)
 {
   const Name& command = request.getName();
-  ndn::FibManagementOptions options;
   const size_t optionCompIndex =
     FIB_MANAGER_COMMAND_PREFIX.size() + 1;
 
@@ -148,49 +148,117 @@
   try
     {
       Block rawOptions(tmpOptionBuffer);
-      options.wireDecode(rawOptions);
+      extractedOptions.wireDecode(rawOptions);
     }
   catch (const ndn::Tlv::Error& e)
     {
       NFD_LOG_INFO("Bad command option parse: " << command);
-      sendResponse(request.getName(), 400, "Malformed command");
-      return;
+      return false;
     }
+  NFD_LOG_DEBUG("Options parsed OK");
+  return true;
+}
 
-  /// \todo authorize command
-  if (false)
-    {
-      NFD_LOG_INFO("Unauthorized command attempt: " << command);
-      sendResponse(request.getName(), 403, "Unauthorized command");
-      return;
-    }
+void
+FibManager::insertEntry(const ndn::FibManagementOptions& options,
+                        ndn::ControlResponse& response)
+{
+  NFD_LOG_DEBUG("insert prefix: " << options.getName());
+  NFD_LOG_INFO("insert result: OK"
+               << " prefix: " << options.getName());
+  std::pair<shared_ptr<fib::Entry>, bool> insertResult = m_managedFib.insert(options.getName());
+  setResponse(response, 200, "OK");
+}
 
-  NFD_LOG_INFO("add-nexthop Name: " << options.getName()
-               << " FaceId: " << options.getFaceId()
-               << " Cost: " << options.getCost());
+void
+FibManager::deleteEntry(const ndn::FibManagementOptions& options,
+                        ndn::ControlResponse& response)
+{
+  NFD_LOG_DEBUG("delete prefix: " << options.getName());
+  NFD_LOG_INFO("delete result: OK"
+               << " prefix: " << options.getName());
+
+  m_managedFib.remove(options.getName());
+  setResponse(response, 200, "OK");
+}
+
+static inline bool
+nextHopEqPredicate(const fib::NextHop& target, const fib::NextHop& hop)
+{
+  return target.getFace()->getId() == hop.getFace()->getId();
+}
+
+void
+FibManager::addNextHop(const ndn::FibManagementOptions& options,
+                          ndn::ControlResponse& response)
+{
+  NFD_LOG_DEBUG("add-nexthop prefix: " << options.getName()
+                << " faceid: " << options.getFaceId()
+                << " cost: " << options.getCost());
 
   shared_ptr<Face> nextHopFace = m_getFace(options.getFaceId());
   if (static_cast<bool>(nextHopFace))
     {
-      std::pair<shared_ptr<fib::Entry>, bool> insertResult = m_managedFib.insert(options.getName());
-      insertResult.first->addNextHop(nextHopFace, options.getCost());
-      sendResponse(request.getName(), 200, "OK");
+      shared_ptr<fib::Entry> entry = m_managedFib.findExactMatch(options.getName());
+      if (static_cast<bool>(entry))
+        {
+          entry->addNextHop(nextHopFace, options.getCost());
+
+          NFD_LOG_INFO("add-nexthop result: OK"
+                       << " prefix:" << options.getName()
+                       << " faceid: " << options.getFaceId()
+                       << " cost: " << options.getCost());
+          setResponse(response, 200, "OK");
+        }
+      else
+        {
+          NFD_LOG_INFO("add-nexthop result: FAIL reason: unknown-prefix: " << options.getName());
+          setResponse(response, 404, "Prefix not found");
+        }
     }
   else
     {
-      NFD_LOG_INFO("Unknown FaceId: " << command);
-      sendResponse(request.getName(), 400, "Malformed command");
+      NFD_LOG_INFO("add-nexthop result: FAIL reason: unknown-faceid: " << options.getFaceId());
+      setResponse(response, 404, "Face not found");
     }
 }
 
 void
-FibManager::fibRemoveNextHop(const Interest& request)
+FibManager::removeNextHop(const ndn::FibManagementOptions& options,
+                             ndn::ControlResponse &response)
 {
+  NFD_LOG_DEBUG("remove-nexthop prefix: " << options.getName()
+                << " faceid: " << options.getFaceId());
 
+  shared_ptr<Face> faceToRemove = m_getFace(options.getFaceId());
+  if (static_cast<bool>(faceToRemove))
+    {
+      shared_ptr<fib::Entry> entry = m_managedFib.findExactMatch(options.getName());
+      if (static_cast<bool>(entry))
+        {
+          entry->removeNextHop(faceToRemove);
+          NFD_LOG_INFO("remove-nexthop result: OK prefix: " << options.getName()
+                       << " faceid: " << options.getFaceId());
+
+          setResponse(response, 200, "OK");
+        }
+      else
+        {
+          NFD_LOG_INFO("remove-nexthop result: FAIL reason: unknown-prefix: "
+                       << options.getName());
+          setResponse(response, 404, "Prefix not found");
+        }
+    }
+  else
+    {
+      NFD_LOG_INFO("remove-nexthop result: FAIL reason: unknown-faceid: "
+                   << options.getFaceId());
+      setResponse(response, 404, "Face not found");
+    }
 }
 
 void
-FibManager::fibStrategy(const Interest& request)
+FibManager::strategy(const ndn::FibManagementOptions& options, ndn::ControlResponse& response)
 {
 
 }