management: nfd::ControlCommand

ControlCommand and its subclasses provide command-specific parameter validation.

refs #1397

Change-Id: Ieca08f5db530c14d1cd16ddd7af17e4ffe6eb344
diff --git a/src/management/nfd-control-command.hpp b/src/management/nfd-control-command.hpp
new file mode 100644
index 0000000..69407c0
--- /dev/null
+++ b/src/management/nfd-control-command.hpp
@@ -0,0 +1,386 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP
+#define NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP
+
+#include "nfd-control-parameters.hpp"
+#include "../util/command-interest-generator.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/** \brief base class of NFD ControlCommand
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+ */
+class ControlCommand
+{
+public:
+  /** \brief represents an error in ControlParameters
+   */
+  class ArgumentError : public std::invalid_argument
+  {
+  public:
+    explicit
+    ArgumentError(const std::string& what)
+      : std::invalid_argument(what)
+    {
+    }
+  };
+
+  /** \return Name prefix of this ControlCommand
+   */
+  const Name&
+  getPrefix() const
+  {
+    return m_prefix;
+  }
+
+  /** \brief make a Command Interest from parameters
+   */
+  Interest
+  makeCommandInterest(const ControlParameters& parameters,
+                      CommandInterestGenerator& commandInterestGenerator) const
+  {
+    this->validateRequest(parameters);
+
+    Name name = m_prefix;
+    name.append(parameters.wireEncode());
+    Interest commandInterest(name);
+    commandInterestGenerator.generate(commandInterest);
+    return commandInterest;
+  }
+
+  /** \brief validate request parameters
+   *  \throw ArgumentError
+   */
+  virtual void
+  validateRequest(const ControlParameters& parameters) const
+  {
+    m_requestValidator.validate(parameters);
+  }
+
+  /** \brief apply default values to missing fields in request
+   */
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const
+  {
+  }
+
+  /** \brief validate response parameters
+   *  \throw ArgumentError
+   */
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    m_responseValidator.validate(parameters);
+  }
+
+  /** \brief apply default values to missing fields in response
+   */
+  virtual void
+  applyDefaultsToResponse(ControlParameters& parameters) const
+  {
+  }
+
+protected:
+  ControlCommand(const std::string& module, const std::string& verb)
+    : m_prefix("ndn:/localhost/nfd")
+  {
+    m_prefix.append(module).append(verb);
+  }
+
+  class FieldValidator
+  {
+  public:
+    FieldValidator()
+    {
+      m_required.resize(CONTROL_PARAMETER_UBOUND);
+      m_optional.resize(CONTROL_PARAMETER_UBOUND);
+    }
+
+    /** \brief declare a required field
+     */
+    FieldValidator&
+    required(ControlParameterField field)
+    {
+      m_required[field] = true;
+      return *this;
+    }
+
+    /** \brief declare an optional field
+     */
+    FieldValidator&
+    optional(ControlParameterField field)
+    {
+      m_optional[field] = true;
+      return *this;
+    }
+
+    /** \brief verify that all required fields are present,
+     *         and all present fields are either required or optional
+     *  \throw ArgumentError
+     */
+    void
+    validate(const ControlParameters& parameters) const
+    {
+      const std::vector<bool>& presentFields = parameters.getPresentFields();
+
+      for (size_t i = 0; i < CONTROL_PARAMETER_UBOUND; ++i) {
+        bool isPresent = presentFields[i];
+        if (m_required[i]) {
+          if (!isPresent) {
+            throw ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is required but missing");
+          }
+        }
+        else if (isPresent && !m_optional[i]) {
+          throw ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is forbidden but present");
+        }
+      }
+    }
+
+  private:
+    std::vector<bool> m_required;
+    std::vector<bool> m_optional;
+  };
+
+protected:
+  /** \brief FieldValidator for request ControlParameters
+   *
+   *  Constructor of subclass should populate this validator.
+   */
+  FieldValidator m_requestValidator;
+  /** \brief FieldValidator for response ControlParameters
+   *
+   *  Constructor of subclass should populate this validator.
+   */
+  FieldValidator m_responseValidator;
+
+private:
+  Name m_prefix;
+};
+
+
+/** \brief represents a faces/create command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Create-a-face
+ */
+class FaceCreateCommand : public ControlCommand
+{
+public:
+  FaceCreateCommand()
+    : ControlCommand("faces", "create")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_URI);
+    m_responseValidator
+      .required(CONTROL_PARAMETER_URI)
+      .required(CONTROL_PARAMETER_FACE_ID);
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateResponse(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+};
+
+
+/** \brief represents a faces/destroy command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Destroy-a-face
+ */
+class FaceDestroyCommand : public ControlCommand
+{
+public:
+  FaceDestroyCommand()
+    : ControlCommand("faces", "destroy")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_FACE_ID);
+    m_responseValidator = m_requestValidator;
+  }
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateRequest(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->validateRequest(parameters);
+  }
+};
+
+
+class FaceLocalControlCommand : public ControlCommand
+{
+protected:
+  explicit
+  FaceLocalControlCommand(const std::string& verb)
+    : ControlCommand("faces", verb)
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE);
+    m_responseValidator = m_requestValidator;
+  }
+};
+
+
+/** \brief represents a faces/enable-local-control command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature
+ */
+class FaceEnableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+  FaceEnableLocalControlCommand()
+    : FaceLocalControlCommand("enable-local-control")
+  {
+  }
+};
+
+
+/** \brief represents a faces/disable-local-control command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature
+ */
+class FaceDisableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+  FaceDisableLocalControlCommand()
+    : FaceLocalControlCommand("disable-local-control")
+  {
+  }
+};
+
+
+/** \brief represents a fib/add-nexthop command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Add-a-nexthop
+ */
+class FibAddNextHopCommand : public ControlCommand
+{
+public:
+  FibAddNextHopCommand()
+    : ControlCommand("fib", "add-nexthop")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID)
+      .optional(CONTROL_PARAMETER_COST);
+    m_responseValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID)
+      .required(CONTROL_PARAMETER_COST);
+  }
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const
+  {
+    if (!parameters.hasCost()) {
+      parameters.setCost(0);
+    }
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateResponse(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+};
+
+
+/** \brief represents a fib/remove-nexthop command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Remove-a-nexthop
+ */
+class FibRemoveNextHopCommand : public ControlCommand
+{
+public:
+  FibRemoveNextHopCommand()
+    : ControlCommand("fib", "remove-nexthop")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID);
+    m_responseValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID);
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateResponse(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+};
+
+
+/** \brief represents a strategy-choice/set command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Set-the-strategy-for-a-namespace
+ */
+class StrategyChoiceSetCommand : public ControlCommand
+{
+public:
+  StrategyChoiceSetCommand()
+    : ControlCommand("strategy-choice", "set")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_STRATEGY);
+    m_responseValidator = m_requestValidator;
+  }
+};
+
+
+/** \brief represents a strategy-choice/set command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Unset-the-strategy-for-a-namespace
+ */
+class StrategyChoiceUnsetCommand : public ControlCommand
+{
+public:
+  StrategyChoiceUnsetCommand()
+    : ControlCommand("strategy-choice", "unset")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME);
+    m_responseValidator = m_requestValidator;
+  }
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateRequest(parameters);
+
+    if (parameters.getName().size() == 0) {
+      throw ArgumentError("Name must not be ndn:/");
+    }
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->validateRequest(parameters);
+  }
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP