summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/high_availability/tests/ha_test.h
blob: 52d5ddf89cbf7c846ca4bb210a29c18b7067775f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// Copyright (C) 2017-2022 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/.

#include <communication_state.h>
#include <ha_config.h>
#include <ha_config_parser.h>
#include <asiolink/io_service.h>
#include <cc/data.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/network_state.h>
#include <hooks/libinfo.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <thread>

namespace isc {
namespace ha {
namespace test {

/// @brief Derivation of the @c CommunicationState class which allows
/// for modifications of poke time.
///
/// @tparam @c CommunicationState4 or @c CommunicationState6.
template<typename StateType>
class NakedCommunicationState : public StateType {
public:

    /// @brief Constructor.
    ///
    /// @param io_service pointer to the IO service object.
    explicit NakedCommunicationState(const asiolink::IOServicePtr& io_service,
                                     const HAConfigPtr& config)
        : StateType(io_service, config) {
    }

    /// @brief Checks if the object was poked recently.
    ///
    /// @return true if the object was poked less than 5 seconds ago,
    /// false otherwise.
    bool isPoked() const {
        return (StateType::getDurationInMillisecs() < 5000);
    }

    using StateType::config_;
    using StateType::timer_;
    using StateType::clock_skew_;
    using StateType::last_clock_skew_warn_;
    using StateType::my_time_at_skew_;
    using StateType::partner_time_at_skew_;
    using StateType::unsent_update_count_;
    using StateType::getRejectedLeaseUpdatesCountFromContainer;
};

/// @brief Type of the NakedCommunicationState for DHCPv4.
typedef NakedCommunicationState<CommunicationState4> NakedCommunicationState4;

/// @brief Type of the pointer to the @c NakedCommunicationState4.
typedef boost::shared_ptr<NakedCommunicationState4> NakedCommunicationState4Ptr;

/// @brief Type of the NakedCommunicationState for DHCPv6.
typedef NakedCommunicationState<CommunicationState6> NakedCommunicationState6;

/// @brief Type of the pointer to the @c NakedCommunicationState6.
typedef boost::shared_ptr<NakedCommunicationState6> NakedCommunicationState6Ptr;

/// @brief General test fixture class for all HA unittests.
///
/// It provides basic functions to load and unload HA hooks library.
/// All test classes should derive from this class.
class HATest : public ::testing::Test {
public:

    /// @brief Constructor.
    HATest();

    /// @brief Destructor.
    virtual ~HATest();

    /// @brief Calls dhcp4_srv_configured callout to set IO service pointer.
    void startHAService();

    /// @brief Runs IO service for a specified amount of time.
    ///
    /// @param ms number of milliseconds for which the IO service should be
    /// run.
    void runIOService(long ms);

    /// @brief Runs IO service until timeout occurs or until provided method
    /// returns true.
    ///
    /// @param ms number of milliseconds for which the IO service should be
    /// run.
    /// @param stop_condition pointer to the function which returns true if
    /// when the IO service should be stopped.
    void runIOService(long ms, std::function<bool()> stop_condition);

    /// @brief Runs IO service in a thread.
    ///
    /// @return Shared pointer to the thread.
    boost::shared_ptr<std::thread> runIOServiceInThread();

    /// @brief Executes commands while running IO service in a thread.
    ///
    /// @param commands pointer to a function to be executed while IO service
    /// is run in thread.
    void testSynchronousCommands(std::function<void()> commands);

protected:

    /// @brief Signals that the IO service is running.
    ///
    /// @param running reference to the flag which is set to true when the
    /// IO service starts running and executes this function.
    /// @param mutex reference to the mutex used for synchronization.
    /// @param condvar reference to condition variable used for synchronization.
    void signalServiceRunning(bool& running, std::mutex& mutex,
                              std::condition_variable& condvar);

public:

    /// @brief Handler for timeout during runIOService invocation.
    ///
    /// @param [out] stop_flag set to true when the handler is invoked.
    void stopIOServiceHandler(bool& stop_flag);

    /// @brief Return HA configuration with three servers in JSON format.
    ///
    /// @param ha_mode HA operation mode (default is load balancing).
    /// @return Pointer to the unparsed configuration.
    data::ConstElementPtr
    createValidJsonConfiguration(const HAConfig::HAMode& ha_mode =
                                 HAConfig::LOAD_BALANCING) const;

    /// @brief Return HA configuration with one primary and two backup
    /// servers in the JSON format.
    ///
    /// @return Pointer to the unparsed configuration.
    data::ConstElementPtr
    createValidPassiveBackupJsonConfiguration() const;

    /// @brief Return HA configuration for a hub in a hub-and-spoke model
    /// in a JSON format.
    ///
    /// @return Pointer to the unparsed configuration.
    data::ConstElementPtr
    createValidHubJsonConfiguration() const;

    /// @brief Return HA configuration with three servers.
    ///
    /// @param ha_mode HA operation mode (default is load balancing).
    /// @return Pointer to the parsed configuration.
    HAConfigPtr createValidConfiguration(const HAConfig::HAMode& ha_mode =
                                         HAConfig::LOAD_BALANCING) const;

    /// @brief Return passive-backup configuration.
    ///
    /// @return Pointer to the parsed configuration.
    HAConfigPtr createValidPassiveBackupConfiguration() const;

    /// @brief Return HA configuration for a hub in a hub-and-spoke model.
    ///
    /// @return Pointer to the parsed configuration.
    HAConfigPtr createValidHubConfiguration() const;

    /// @brief Checks the status code and message against expected values.
    ///
    /// @param answer Element set containing an integer response and string
    /// comment.
    /// @param exp_status Status code against which to compare the status.
    /// @param exp_txt Expected text (not checked if empty)
    void checkAnswer(const isc::data::ConstElementPtr& answer,
                     const int exp_status,
                     const std::string& exp_txt = "");

    /// @brief Creates an identifier of arbitrary size with random values.
    ///
    /// This function is useful in generating random client identifiers and
    /// HW addresses for load balancing tests.
    ///
    /// @param key_size Size of the generated random identifier.
    std::vector<uint8_t> randomKey(const size_t key_size) const;

    /// @brief Generates simple DHCPv4 message.
    ///
    /// @param msg_type DHCPv4 message type to be created.
    /// @param hw_address_seed value from which HW address will be generated.
    /// @param client_id_seed value from which client identifier will be
    /// generated.
    /// @param secs value to be stored in the "secs" field of the DHCPv4 message.
    ///
    /// @return Pointer to the created message.
    dhcp::Pkt4Ptr createMessage4(const uint8_t msg_type,
                                 const uint8_t hw_address_seed,
                                 const uint8_t client_id_seed,
                                 const uint16_t secs) const;

    /// @brief Creates test DHCPv4 query instance.
    ///
    /// @param hw_address_text HW address to be included in the query. It is
    /// used in load balancing.
    ///
    /// @return Pointer to the DHCPv4 query instance.
    dhcp::Pkt4Ptr createQuery4(const std::string& hw_address_text) const;

    /// @brief Creates test DHCPv4 query instance.
    ///
    /// @param hw_address HW address to be included in the query. It is used
    /// in load balancing.
    /// @param client_id optional client identifier.
    dhcp::Pkt4Ptr createQuery4(const std::vector<uint8_t>& hw_address,
                               const std::vector<uint8_t>& client_id =
                               std::vector<uint8_t>()) const;

    /// @brief Creates test DHCPv6 query instance.
    ///
    /// @param duid DUI to be included in the query. It is used in load balancing.
    dhcp::Pkt6Ptr createQuery6(const std::vector<uint8_t>& duid) const;

    /// @brief Generates simple DHCPv6 message.
    ///
    /// @param msg_type DHCPv6 message type to be created.
    /// @param duid value from which DUID will be generated.
    /// @param elapsed_time value of the Elapsed Time option.
    ///
    /// @return Pointer to the created message.
    dhcp::Pkt6Ptr createMessage6(const uint8_t msg_type,
                                 const uint8_t duid_seed,
                                 const uint16_t elapsed_time) const;

    /// @brief Sets the DHCP multi-threading configuration in staging SrvConfig.
    ///
    /// @param enable_multi_threading value that maps to enable-multi-threading.
    /// @param thread_pool_size value that maps to thread-pool-size.
    /// @param queue_size value that maps to queue-size.
    void setDHCPMultiThreadingConfig(bool enable_multi_threading,
                                     uint32_t thread_pool_size = 0,
                                     uint32_t queue_size = 16);

    /// @brief Replace a pattern in a configuration.
    ///
    /// @param config Configuration to patch.
    /// @param from String to replace.
    /// @param repl String which replaces all occurrences of from.
    /// @result A copy of config where all occurrences of from were replaced
    /// by repl.
    std::string replaceInConfig(const std::string& config,
                                const std::string& from,
                                const std::string& repl) {
        std::string result(config);
        if (from.empty()) {
            return (result);
        }
        for (;;) {
            size_t where = result.find(from);
            if (where == std::string::npos) {
                return (result);
            }
            result.replace(where, from.size(), repl);
        }
        return (result);
    }

    /// @brief Constructs JSON string for HA "multi-threading" element.
    ///
    /// Constructs a JSON string with the following content:
    ///
    /// ```
    /// "multi-threading" {
    ///     "enable-multi-threading": <bool>,
    ///     "dedicated-http-listener": <bool>,
    ///     "http-listener-threads": <int>,
    ///     "http-client-threads": <int>
    /// }"
    /// ```
    ///
    /// @param enable_multi_threading value for enable-multi-threading.
    /// @param http_dedicated_listener value for dedicated-http-listener.
    /// @param http_listener_threads value for http-listener-threads
    /// @param http_client_threads value for http-client-threads
    ///
    /// @return JSON string
    std::string makeHAMtJson(bool enable_multi_threading,
                             bool http_dedicated_listener,
                             uint32_t http_listener_threads,
                             uint32_t http_client_threads);

    /// @brief Creates test DHCPv6 query instance.
    ///
    /// @param duid_text DUID to be included in the query. It is used in load
    /// balancing.
    ///
    /// @return Pointer to the DHCPv6 query instance.
    dhcp::Pkt6Ptr createQuery6(const std::string& duid_text) const;

    /// @brief Pointer to the IO service used in the tests.
    asiolink::IOServicePtr io_service_;

    /// @brief Object holding a state of the DHCP service.
    dhcp::NetworkStatePtr network_state_;
};

} // end of namespace isc::ha::test
} // end of namespace isc::ha
} // end of namespace isc