diff options
Diffstat (limited to 'src/bin/dhcp4/tests/dhcp4_test_utils.h')
-rw-r--r-- | src/bin/dhcp4/tests/dhcp4_test_utils.h | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h new file mode 100644 index 0000000..fa4cf7e --- /dev/null +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -0,0 +1,799 @@ +// Copyright (C) 2013-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// @file dhcp4_test_utils.h +/// +/// @brief This file contains utility classes used for DHCPv4 server testing + +#ifndef DHCP4_TEST_UTILS_H +#define DHCP4_TEST_UTILS_H + +#include <gtest/gtest.h> +#include <dhcp/iface_mgr.h> +#include <dhcp/option4_addrlst.h> +#include <dhcp/pkt4.h> +#include <dhcp/pkt_filter.h> +#include <dhcp/pkt_filter_inet.h> +#include <dhcpsrv/alloc_engine.h> +#include <dhcpsrv/subnet.h> +#include <dhcpsrv/lease.h> +#include <dhcpsrv/lease_mgr_factory.h> +#include <dhcp4/dhcp4_srv.h> +#include <dhcp4/parser_context.h> +#include <asiolink/io_address.h> +#include <cc/command_interpreter.h> +#include <util/multi_threading_mgr.h> +#include <list> + +#include <boost/shared_ptr.hpp> + +namespace isc { +namespace dhcp { +namespace test { + +/// @brief Dummy Packet Filtering class. +/// +/// This class reports capability to respond directly to the client which +/// doesn't have address configured yet. +/// +/// All packet and socket handling functions do nothing because they are not +/// used in unit tests. +class PktFilterTest : public PktFilter { +public: + + /// @brief Constructor. + /// + /// Sets the 'direct response' capability to true. + PktFilterTest() + : direct_resp_supported_(true) { + } + + /// @brief Reports 'direct response' capability. + /// + /// @return always true. + virtual bool isDirectResponseSupported() const { + return (direct_resp_supported_); + } + + /// Does nothing. + virtual SocketInfo openSocket(Iface&, + const isc::asiolink::IOAddress& addr, + const uint16_t port, const bool, const bool) { + return (SocketInfo(addr, port, 0)); + } + + /// Does nothing. + virtual Pkt4Ptr receive(Iface&, const SocketInfo&) { + return Pkt4Ptr(); + } + + /// Does nothing. + virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) { + return (0); + } + + /// @brief Holds a boolean value which indicates whether direct response + /// capability is supported (true) or not (false). + bool direct_resp_supported_; + +}; + +typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr; + +/// Forward definition for Dhcp4Client defined in dhcp4_client.h +/// dhcp4_client.h includes dhcp_test_utils.h (this file), so to avoid +/// circular dependencies, we need a forward class declaration. +class Dhcp4Client; + +/// @brief "Naked" DHCPv4 server, exposes internal fields +class NakedDhcpv4Srv: public Dhcpv4Srv { +public: + + /// @brief Constructor. + /// + /// This constructor disables default modes of operation used by the + /// Dhcpv4Srv class: + /// - Send/receive broadcast messages through sockets on interfaces + /// which support broadcast traffic. + /// - Direct DHCPv4 traffic - communication with clients which do not + /// have IP address assigned yet. + /// + /// Enabling these modes requires root privileges so they must be + /// disabled for unit testing. + /// + /// Note, that disabling broadcast options on sockets does not impact + /// the operation of these tests because they use local loopback + /// interface which doesn't have broadcast capability anyway. It rather + /// prevents setting broadcast options on other (broadcast capable) + /// sockets which are opened on other interfaces in Dhcpv4Srv constructor. + /// + /// The Direct DHCPv4 Traffic capability can be disabled here because + /// it is tested with PktFilterLPFTest unittest. The tests which belong + /// to PktFilterLPFTest can be enabled on demand when root privileges can + /// be guaranteed. + /// + /// @param port port number to listen on; the default value 0 indicates + /// that sockets should not be opened. + NakedDhcpv4Srv(uint16_t port = 0) + : Dhcpv4Srv(port, false, false) { + // Create a default lease database backend. + std::string dbconfig = "type=memfile universe=4 persist=false"; + isc::dhcp::LeaseMgrFactory::create(dbconfig); + // Create fixed server id. + server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, + asiolink::IOAddress("192.0.3.1"))); + LeaseMgr::setIOService(getIOService()); + } + + /// @brief Returns fixed server identifier assigned to the naked server + /// instance. + OptionPtr getServerID() const { + return (server_id_); + } + + /// @brief fakes packet reception + /// @param timeout ignored + /// + /// The method receives all packets queued in receive queue, one after + /// another. Once the queue is empty, it initiates the shutdown procedure. + /// + /// See fake_received_ field for description + virtual Pkt4Ptr receivePacket(int /*timeout*/) { + // If there is anything prepared as fake incoming traffic, use it + if (!fake_received_.empty()) { + Pkt4Ptr pkt = fake_received_.front(); + fake_received_.pop_front(); + return (pkt); + } + + // Make sure the server processed all packets in MT. + isc::util::MultiThreadingMgr::instance().getThreadPool().wait(3); + + // If not, just trigger shutdown and return immediately + shutdown(); + return (Pkt4Ptr()); + } + + /// @brief fake packet sending + /// + /// Pretend to send a packet, but instead just store it in fake_send_ list + /// where test can later inspect server's response. + virtual void sendPacket(const Pkt4Ptr& pkt) { + isc::util::MultiThreadingLock lock(mutex_); + fake_sent_.push_back(pkt); + } + + /// @brief fake receive packet from server + /// + /// The client uses this packet as a reply from the server. + /// + /// @return The received packet. + Pkt4Ptr receiveOneMsg() { + // Make sure the server processed all packets. + isc::util::MultiThreadingMgr::instance().getThreadPool().wait(2); + + // Lock the mutex for the rest of the function. + isc::util::MultiThreadingLock lock(mutex_); + + // Return empty pointer if server hasn't responded. + if (fake_sent_.empty()) { + return (Pkt4Ptr()); + } + + Pkt4Ptr msg = fake_sent_.front(); + fake_sent_.pop_front(); + return (msg); + } + + /// @brief adds a packet to fake receive queue + /// + /// See fake_received_ field for description + void fakeReceive(const Pkt4Ptr& pkt) { + fake_received_.push_back(pkt); + } + + virtual ~NakedDhcpv4Srv() { + } + + /// @brief Runs processing DHCPDISCOVER. + /// + /// @param discover a message received from client + /// @return DHCPOFFER message or null + Pkt4Ptr processDiscover(Pkt4Ptr& discover) { + AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4()); + earlyGHRLookup(discover, context); + return (processDiscover(discover, context)); + } + + /// @brief Runs processing DHCPREQUEST. + /// + /// @param request a message received from client + /// @return DHCPACK or DHCPNAK message + Pkt4Ptr processRequest(Pkt4Ptr& request) { + AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4()); + earlyGHRLookup(request, context); + return (processRequest(request, context)); + } + + /// @brief Runs processing DHCPRELEASE. + /// + /// @param release message received from client + void processRelease(Pkt4Ptr& release) { + AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4()); + earlyGHRLookup(release, context); + processRelease(release, context); + } + + /// @brief Runs processing DHCPDECLINE. + /// + /// @param decline message received from client + void processDecline(Pkt4Ptr& decline) { + AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4()); + earlyGHRLookup(decline, context); + processDecline(decline, context); + } + + /// @brief Runs processing DHCPINFORM. + /// + /// @param inform a message received from client + /// @return DHCPACK message + Pkt4Ptr processInform(Pkt4Ptr& inform) { + AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4()); + earlyGHRLookup(inform, context); + return (processInform(inform, context)); + } + + /// @brief Dummy server identifier option used by various tests. + OptionPtr server_id_; + + /// @brief packets we pretend to receive. + /// + /// Instead of setting up sockets on interfaces that change between OSes, it + /// is much easier to fake packet reception. This is a list of packets that + /// we pretend to have received. You can schedule new packets to be received + /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods. + std::list<Pkt4Ptr> fake_received_; + + /// @brief packets we pretend to send. + std::list<Pkt4Ptr> fake_sent_; + + using Dhcpv4Srv::adjustIfaceData; + using Dhcpv4Srv::appendServerID; + using Dhcpv4Srv::processDiscover; + using Dhcpv4Srv::processRequest; + using Dhcpv4Srv::processRelease; + using Dhcpv4Srv::processDecline; + using Dhcpv4Srv::processInform; + using Dhcpv4Srv::processClientName; + using Dhcpv4Srv::createNameChangeRequests; + using Dhcpv4Srv::acceptServerId; + using Dhcpv4Srv::sanityCheck; + using Dhcpv4Srv::srvidToString; + using Dhcpv4Srv::classifyPacket; + using Dhcpv4Srv::deferredUnpack; + using Dhcpv4Srv::accept; + using Dhcpv4Srv::acceptMessageType; + using Dhcpv4Srv::selectSubnet; + using Dhcpv4Srv::setSendResponsesToSource; + using Dhcpv4Srv::VENDOR_CLASS_PREFIX; + using Dhcpv4Srv::shutdown_; + using Dhcpv4Srv::alloc_engine_; + using Dhcpv4Srv::server_port_; + using Dhcpv4Srv::client_port_; + + /// @brief Mutex to protect the packet buffers. + std::mutex mutex_; +}; + +// We need to pass one reference to the Dhcp4Client, which is defined in +// dhcp4_client.h. That header includes this file. To avoid circular +// dependencies, we use forward declaration here. +class Dhcp4Client; + +/// @brief Base class for DHCPv4 server testing. +/// +/// Currently it configures the test data path directory in +/// the @c CfgMgr. When the object is destroyed, the original +/// path is reverted. +class BaseServerTest : public ::testing::Test { +public: + + /// @brief Constructor. + BaseServerTest(); + + /// @brief Destructor. + virtual ~BaseServerTest(); + +private: + + /// @brief Holds the original data directory. + std::string original_datadir_; + +}; + +class Dhcpv4SrvTest : public BaseServerTest { +public: + /// @brief Specifies expected outcome + enum ExpectedResult { + SHOULD_PASS, // pass = accept decline, move lease to declined state. + SHOULD_FAIL // fail = reject the decline + }; + + /// @brief Specifies if lease affinity is enabled or disabled + enum LeaseAffinity { + LEASE_AFFINITY_ENABLED, + LEASE_AFFINITY_DISABLED + }; + + class Dhcpv4SrvMTTestGuard { + public: + Dhcpv4SrvMTTestGuard(Dhcpv4SrvTest& test, bool mt_enabled) : test_(test) { + test_.setMultiThreading(mt_enabled); + } + ~Dhcpv4SrvMTTestGuard() { + test_.setMultiThreading(false); + } + Dhcpv4SrvTest& test_; + }; + + /// @brief Constructor + /// + /// Initializes common objects used in many tests. + /// Also sets up initial configuration in CfgMgr. + Dhcpv4SrvTest(); + + /// @brief Destructor + /// + /// Removes existing configuration. + virtual ~Dhcpv4SrvTest(); + + /// @brief Add 'Parameter Request List' option to the packet. + /// + /// This function adds PRL option comprising the following option codes: + /// - 5 - Name Server + /// - 15 - Domain Name + /// - 7 - Log Server + /// - 8 - Quotes Server + /// - 9 - LPR Server + /// + /// @param pkt packet to add PRL option to. + void addPrlOption(Pkt4Ptr& pkt); + + /// @brief Used to configure a server for tests. + /// + /// A wrapper over configureDhcp4Server() to which any other + /// simulations of production code are added. + /// + /// @brief server the server being tested + /// @brief config the configuration the server is configured with + /// + /// @return a JSON-formatted status of the reconfiguration + static ConstElementPtr configure(Dhcpv4Srv& server, isc::data::ConstElementPtr config); + + /// @brief Configures options being requested in the PRL option. + /// + /// The lpr-servers option is NOT configured here although it is + /// added to the 'Parameter Request List' option in the + /// \ref addPrlOption. When requested option is not configured + /// the server should not return it in its response. The goal + /// of not configuring the requested option is to verify that + /// the server will not return it. + void configureRequestedOptions(); + + /// @brief Configures server identifier at different levels. + void configureServerIdentifier(); + + /// @brief checks that the response matches request + /// + /// @param q query (client's message) + /// @param a answer (server's message) + void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a); + + /// @brief Check that certain basic (always added when lease is acquired) + /// are present in a message. + /// + /// @param pkt A message to be checked. + /// @return Assertion result which indicates whether test passed or failed. + ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt); + + /// @brief Check that certain basic (always added when lease is acquired) + /// are not present. + /// + /// @param pkt A packet to be checked. + /// @return Assertion result which indicates whether test passed or failed. + ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt); + + /// @brief Check that certain requested options are present in the message. + /// + /// @param pkt A message to be checked. + /// @return Assertion result which indicates whether test passed or failed. + ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt); + + /// @brief Check that certain options (requested with PRL option) + /// are not present. + /// + /// @param pkt A packet to be checked. + /// @return Assertion result which indicates whether test passed or failed. + ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt); + + /// @brief generates client-id option + /// + /// Generate client-id option of specified length + /// Ids with different lengths are sufficient to generate + /// unique ids. If more fine grained control is required, + /// tests generate client-ids on their own. + /// Sets client_id_ field. + /// @param size size of the client-id to be generated + OptionPtr generateClientId(size_t size = 4); + + /// @brief generate hardware address + /// + /// @param size size of the generated MAC address + /// @param pointer to Hardware Address object + HWAddrPtr generateHWAddr(size_t size = 6); + + /// @brief Convenience method for making a server identifier option instance. + /// + /// @param address IP address to add to the option + /// + /// @return Pointer to the newly constructed option. + OptionCustomPtr makeServerIdOption(const isc::asiolink::IOAddress& address); + + /// @brief Convenience method for making a fqdn list option instance. + /// + /// @return Pointer to the newly constructed option. + OptionPtr makeFqdnListOption(); + + /// Check that address was returned from proper range, that its lease + /// lifetime is correct, that T1 and T2 are returned properly + /// @param rsp response to be checked + /// @param subnet subnet that should be used to verify assigned address + /// and options + /// @param t1_present check that t1 must be present (true) or must not be + /// present (false) + /// @param t2_present check that t2 must be present (true) or must not be + /// present (false) + /// @param expected_valid check that lease lifetime has the not-zero + /// expected value (zero value means that do not check). + void checkAddressParams(const Pkt4Ptr& rsp, const Subnet4Ptr subnet, + bool t1_present = false, + bool t2_present = false, + uint32_t expected_valid = 0); + + /// @brief Basic checks for generated response (message type and trans-id). + /// + /// @param rsp response packet to be validated + /// @param expected_message_type expected message type + /// @param expected_transid expected transaction-id + void checkResponse(const Pkt4Ptr& rsp, int expected_message_type, + uint32_t expected_transid); + + /// @brief Checks if the lease sent to client is present in the database + /// + /// @param rsp response packet to be validated + /// @param client_id expected client-identifier (or NULL) + /// @param HWAddr expected hardware address (not used now) + /// @param expected_addr expected address + Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id, + const HWAddrPtr&, + const isc::asiolink::IOAddress& expected_addr); + + /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id + /// + /// @param rsp response packet to be validated + /// @param expected_srvid expected value of server-id + void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid); + + /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id + /// + /// This method follows values reported by CfgMgr in echoClientId() method. + /// Depending on its configuration, the client-id is either mandatory or + /// forbidden to appear in the response. + /// + /// @param rsp response packet to be validated + /// @param expected_clientid expected value of client-id + void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid); + + /// @brief Checks the value of the dhcp-server-identifier option in a packet + /// + /// @param packet packet to test + /// @param expected_address IP address the packet's option should contain + void checkServerIdOption(const Pkt4Ptr& packet, const isc::asiolink::IOAddress& expected_address); + + /// @brief Create packet from output buffer of another packet. + /// + /// This function creates a packet using an output buffer from another + /// packet. This imitates reception of a packet from the wire. The + /// unpack function is then called to parse packet contents and to + /// create a collection of the options carried by this packet. + /// + /// This function is useful for unit tests which verify that the received + /// packet is parsed correctly. In those cases it is usually inappropriate + /// to create an instance of the packet, add options, set packet + /// fields and use such packet as an input to processDiscover or + /// processRequest function. This is because, such a packet has certain + /// options already initialized and there is no way to verify that they + /// have been initialized when packet instance was created or wire buffer + /// processing. By creating a packet from the buffer we guarantee that the + /// new packet is entirely initialized during wire data parsing. + /// + /// @param src_pkt A source packet, to be copied. + /// @param [out] dst_pkt A destination packet. + /// + /// @return assertion result indicating if a function completed with + /// success or failure. + static ::testing::AssertionResult + createPacketFromBuffer(const isc::dhcp::Pkt4Ptr& src_pkt, + isc::dhcp::Pkt4Ptr& dst_pkt); + + /// @brief Tests if Discover or Request message is processed correctly + /// + /// This test verifies that the Parameter Request List option is handled + /// correctly, i.e. it checks that certain options are present in the + /// server's response when they are requested and that they are not present + /// when they are not requested or NAK occurs. + /// + /// @todo We need an additional test for PRL option using real traffic + /// capture. + /// + /// @param msg_type DHCPDISCOVER or DHCPREQUEST + void testDiscoverRequest(const uint8_t msg_type); + + /// @brief Create test which verifies server identifier. + /// + /// @param expected_server_id expected server identifier + /// @param query the query used to get associated client classes + /// @param requested the requested address + /// @param server_id server identifier + void buildCfgOptionTest(isc::asiolink::IOAddress expected_server_id, + Pkt4Ptr& query, + isc::asiolink::IOAddress requested, + isc::asiolink::IOAddress server_id); + + /// @brief Runs DHCPv4 configuration from the JSON string. + /// + /// @param config String holding server configuration in JSON format. + /// @param commit A boolean flag indicating if the new configuration + /// should be committed (if true), or not (if false). + /// @param open_sockets A boolean flag indicating if sockets should + /// be opened (if true), or not (if false). + /// @param create_managers A boolean flag indicating if managers should be + /// recreated. + /// @param test A boolean flag which indicates if only testing config. + /// @param lease_affinity A flag which indicates if lease affinity should + /// be enabled or disabled. + void configure(const std::string& config, + const bool commit = true, + const bool open_sockets = true, + const bool create_managers = true, + const bool test = false, + const LeaseAffinity lease_affinity = LEASE_AFFINITY_DISABLED); + + /// @brief Configure the DHCPv4 server using the JSON string. + /// + /// @param config String holding server configuration in JSON format. + /// @param srv Instance of the server to be configured. + /// @param commit A boolean flag indicating if the new configuration + /// should be committed (if true), or not (if false). + /// @param open_sockets A boolean flag indicating if sockets should + /// be opened (if true), or not (if false). + /// @param create_managers A boolean flag indicating if managers should be + /// recreated. + /// @param test A boolean flag which indicates if only testing config. + /// @param lease_affinity A flag which indicates if lease affinity should + /// be enabled or disabled. + void configure(const std::string& config, + NakedDhcpv4Srv& srv, + const bool commit = true, + const bool open_sockets = true, + const bool create_managers = true, + const bool test = false, + const LeaseAffinity lease_affinity = LEASE_AFFINITY_DISABLED); + + /// @brief Configure specified DHCP server using JSON string. + /// + /// @param config String holding server configuration in JSON format. + /// @param srv Instance of the server to be configured. + /// @param commit A boolean flag indicating if the new configuration + /// should be committed (if true), or not (if false). + /// @param exp_rcode expected status code (default = 0 (success)) + /// @return (a pair of status code and a string with result) + std::pair<int, std::string> + configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv, + const bool commit = true, const int exp_rcode = 0); + + /// @brief Pretends a packet of specified type was received. + /// + /// Instantiates fake network interfaces, configures passed Dhcpv4Srv, + /// then creates a message of specified type and sends it to the + /// server and then checks whether expected statistics were set + /// appropriately. + /// + /// @param srv the DHCPv4 server to be used + /// @param config JSON configuration to be used + /// @param pkt_type type of the packet to be faked + /// @param stat_name name of the expected statistic + void pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& config, + uint8_t pkt_type, const std::string& stat_name); + + /// @brief Create @c Dhcpv4Exchange from client's query. + Dhcpv4Exchange createExchange(const Pkt4Ptr& query); + + /// @brief Performs 4-way exchange to obtain new lease. + /// + /// This is used as a preparatory step for Decline operation. + /// + /// @param client Client to be used to obtain a lease. + void acquireLease(Dhcp4Client& client); + + /// @brief Tests if the acquired lease is or is not declined. + /// + /// @param client Dhcp4Client instance + /// @param hw_address_1 HW Address to be used to acquire the lease. + /// @param client_id_1 Client id to be used to acquire the lease. + /// @param hw_address_2 HW Address to be used to decline the lease. + /// @param client_id_2 Client id to be used to decline the lease. + /// @param expected_result SHOULD_PASS if the lease is expected to + /// be successfully declined, or SHOULD_FAIL if the lease is expected + /// to not be declined. + /// @param config_index specifies which config index should be used: + /// 0 - memfile, 1 - mysql, 2 - pgsql + void acquireAndDecline(Dhcp4Client& client, + const std::string& hw_address_1, + const std::string& client_id_1, + const std::string& hw_address_2, + const std::string& client_id_2, + ExpectedResult expected_result, + uint8_t config_index = 0); + + /// @brief Checks if received relay agent info option is echoed back to the + /// client. + void relayAgentInfoEcho(); + + /// @brief Checks if received bad relay agent info option is not echoed back + /// to the client. + void badRelayAgentInfoEcho(); + + /// @brief Checks if client port can be overridden in packets being sent. + void portsClientPort(); + + /// @brief Checks if server port can be overridden in packets being sent. + void portsServerPort(); + + /// @brief Check if example files contain valid configuration. + void checkConfigFiles(); + + /// @brief Check if the server configuration stored in file is valid. + /// + /// @param path The path to the configuration file. + void loadConfigFile(const std::string& path); + + /// @brief This function cleans up after the test. + virtual void TearDown(); + + /// @brief Set multi-threading mode. + void setMultiThreading(bool enabled) { + multi_threading_ = enabled; + } + + /// @brief A subnet used in most tests. + Subnet4Ptr subnet_; + + /// @brief A pool used in most tests. + Pool4Ptr pool_; + + /// @brief A client-id used in most tests. + ClientIdPtr client_id_; + + /// @brief Return code + int rcode_; + + /// @brief Comment received from configuration. + isc::data::ConstElementPtr comment_; + + /// @brief Server object under test. + NakedDhcpv4Srv srv_; + + /// @brief The multi-threading flag. + bool multi_threading_; +}; + +/// @brief Patch the server config to add interface-config/re-detect=false +/// @param json the server config +inline void +disableIfacesReDetect(isc::data::ConstElementPtr json) { + isc::data::ConstElementPtr ifaces_cfg = json->get("interfaces-config"); + if (ifaces_cfg) { + isc::data::ElementPtr mutable_cfg = + boost::const_pointer_cast<isc::data::Element>(ifaces_cfg); + mutable_cfg->set("re-detect", isc::data::Element::create(false)); + } +} + +/// @brief Patch the server config to add multi-threading/enable-multi-threading +/// @param json the server config +inline void +configureMultiThreading(bool enabled, isc::data::ConstElementPtr json) { + isc::data::ConstElementPtr multi_threading = json->get("multi-threading"); + if (!multi_threading) { + isc::data::ElementPtr mutable_cfg = + boost::const_pointer_cast<isc::data::Element>(json); + multi_threading = isc::data::Element::createMap(); + mutable_cfg->set("multi-threading", multi_threading); + } + + isc::data::ElementPtr mutable_cfg = + boost::const_pointer_cast<isc::data::Element>(multi_threading); + if (enabled) { + mutable_cfg->set("enable-multi-threading", isc::data::Element::create(true)); + mutable_cfg->set("thread-pool-size", isc::data::Element::create(4)); + mutable_cfg->set("packet-queue-size", isc::data::Element::create(4)); + } else { + mutable_cfg->set("enable-multi-threading", isc::data::Element::create(false)); + } +} + +/// @brief Runs parser in JSON mode, useful for parser testing +/// +/// @param in string to be parsed +/// @return ElementPtr structure representing parsed JSON +inline isc::data::ElementPtr +parseJSON(const std::string& in) { + isc::dhcp::Parser4Context ctx; + return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_JSON)); +} + +/// @brief Runs parser in Dhcp4 mode +/// +/// This is a simplified Dhcp4 mode, so no outer { } and "Dhcp4" is +/// needed. This format is used by most of the tests. +/// +/// @param in string to be parsed +/// @param verbose display the exception message when it fails +/// @return ElementPtr structure representing parsed JSON +inline isc::data::ElementPtr +parseDHCP4(const std::string& in, bool verbose = false) { + try { + isc::dhcp::Parser4Context ctx; + isc::data::ElementPtr json; + json = ctx.parseString(in, isc::dhcp::Parser4Context::SUBPARSER_DHCP4); + disableIfacesReDetect(json); + return (json); + } + catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Runs parser in option definition mode +/// +/// This function parses specified text as JSON that defines option definitions. +/// +/// @param in string to be parsed +/// @param verbose display the exception message when it fails +/// @return ElementPtr structure representing parsed JSON +inline isc::data::ElementPtr +parseOPTION_DEFS(const std::string& in, bool verbose = false) { + try { + isc::dhcp::Parser4Context ctx; + return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_OPTION_DEFS)); + } + catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +} // end of isc::dhcp::test namespace +} // end of isc::dhcp namespace +} // end of isc namespace + +#endif // DHCP4_TEST_UTILS_H |