client_list.h 20.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef DATASRC_CONTAINER_H
#define DATASRC_CONTAINER_H

18
19
#include <util/memory_segment.h>

20
#include <dns/name.h>
21
#include <dns/rrclass.h>
22
23
#include <cc/data.h>
#include <exceptions/exceptions.h>
24
#include "memory/zone_table_segment.h"
25

26
#include <vector>
27
#include <boost/shared_ptr.hpp>
28
#include <boost/scoped_ptr.hpp>
29
30
31
32
33
34
35
36
37
#include <boost/noncopyable.hpp>

namespace isc {
namespace datasrc {

class ZoneFinder;
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
class DataSourceClient;
typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
38
39
40
class DataSourceClientContainer;
typedef boost::shared_ptr<DataSourceClientContainer>
    DataSourceClientContainerPtr;
41
42
43
44
// XXX: it's better to even hide the existence of the "memory" namespace.
// We should probably consider pimpl for details of ConfigurableClientList
// and hide real definitions except for itself and tests.
namespace memory {
45
class InMemoryClient;
46
class ZoneWriter;
47
}
48

49
50
/// \brief Segment status of the cache
///
51
52
/// Describes the status in which the memory segment for the in-memory cache of
// /given data source is.
53
54
enum MemorySegmentState {
    /// \brief No segment used for this data source.
55
56
57
58
    ///
    /// This is usually a result of the cache being disabled.
    SEGMENT_UNUSED,

59
60
    /// \brief It is a mapped segment and we wait for information how to map
    ///     it.
61
62
63
    SEGMENT_WAITING,

    /// \brief The segment is ready to be used.
64
    SEGMENT_INUSE
65
66
};

67
68
69
70
71
72
73
74
75
/// \brief The type of the memory segment in cache
enum MemorySegmentType {
    /// \brief A locally loaded, unshared cache. Normal memory.
    SEGMENT_LOCAL,

    /// \brief A file image mapped into memory
    SEGMENT_FILE
};

76
77
78
/// \brief Status of one data source.
///
/// This indicates the status a data soure is in. It is used with segment
79
/// and cache management, to discover the data sources that need external
80
/// mapping or local loading.
81
82
83
///
/// In future, it may be extended for other purposes, such as performing an
/// operation on named data source.
84
85
86
87
class DataSourceStatus {
public:
    /// \brief Constructor
    ///
88
89
90
91
    /// Sets initial values. It doesn't matter what is provided for the type
    /// if state is SEGMENT_UNUSED, the value is effectively ignored.
    DataSourceStatus(const std::string& name, MemorySegmentState state,
                     MemorySegmentType type) :
92
        name_(name),
93
94
        state_(state),
        type_(type)
95
    {}
96

97
    /// \brief Get the segment state
98
99
100
    MemorySegmentState getSegmentState() const {
        return (state_);
    }
101

102
    /// \brief Get the segment type
103
    ///
104
    /// \throw isc::InvalidOperation if called and state is SEGMENT_UNUSED.
105
106
    MemorySegmentType getSegmentType() const {
        if (getSegmentState() == SEGMENT_UNUSED) {
107
108
            isc_throw(isc::InvalidOperation,
                      "No segment used, no type therefore.");
109
110
111
112
        }
        return (type_);
    }

113
    /// \brief Get the name.
114
115
116
117
118
119
    const std::string& getName() const {
        return (name_);
    }
private:
    std::string name_;
    MemorySegmentState state_;
120
    MemorySegmentType type_;
121
122
};

123
/// \brief The list of data source clients.
124
///
125
/// The purpose of this class is to hold several data source clients and search
126
127
/// through them to find one containing a zone best matching a request.
///
128
/// All the data source clients should be for the same class. If you need
129
/// to handle multiple classes, you need to create multiple separate lists.
130
///
131
132
133
134
/// This is an abstract base class. It is not expected we would use multiple
/// implementation inside the servers (but it is not forbidden either), we
/// have it to allow easy testing. It is possible to create a mock-up class
/// instead of creating a full-blown configuration. The real implementation
135
136
/// is the ConfigurableClientList.
class ClientList : public boost::noncopyable {
137
protected:
138
    /// \brief Constructor.
139
140
141
    ///
    /// It is protected to prevent accidental creation of the abstract base
    /// class.
142
    ClientList() {}
143
public:
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
144
145
    /// \brief Virtual destructor
    virtual ~ClientList() {}
146
    /// \brief Structure holding the (compound) result of find.
147
148
149
150
    ///
    /// As this is read-only structure, we don't bother to create accessors.
    /// Instead, all the member variables are defined as const and can be
    /// accessed directly.
151
    struct FindResult {
152
153
154
155
156
157
158
159
160
161
        /// \brief Internal class for holding a reference.
        ///
        /// This is used to make sure the data source client isn't released
        /// too soon.
        ///
        /// \see life_keeper_;
        class LifeKeeper {
        public:
            virtual ~LifeKeeper() {};
        };
162
163
164
165
        /// \brief Constructor.
        ///
        /// It simply fills in the member variables according to the
        /// parameters. See the member descriptions for their meaning.
166
        FindResult(DataSourceClient* dsrc_client, const ZoneFinderPtr& finder,
167
168
                   bool exact_match,
                   const boost::shared_ptr<LifeKeeper>& life_keeper) :
169
            dsrc_client_(dsrc_client),
170
            finder_(finder),
171
172
            exact_match_(exact_match),
            life_keeper_(life_keeper)
173
        {}
174

175
176
177
        /// \brief Negative answer constructor.
        ///
        /// This conscructs a result for negative answer. Both pointers are
178
        /// NULL, and exact_match_ is false.
179
        FindResult() :
180
            dsrc_client_(NULL),
181
            exact_match_(false)
182
        {}
183

184
185
186
187
        /// \brief Comparison operator.
        ///
        /// It is needed for tests and it might be of some use elsewhere
        /// too.
188
        bool operator ==(const FindResult& other) const {
189
        return (dsrc_client_ == other.dsrc_client_ &&
190
191
                finder_ == other.finder_ &&
                exact_match_ == other.exact_match_);
192
        }
193

194
        /// \brief The found data source client.
195
        ///
196
197
        /// The client of the data source containing the best matching zone.
        /// If no such data source exists, this is NULL pointer.
198
        ///
199
        /// Note that the pointer is valid only as long the ClientList which
200
201
202
        /// returned the pointer is alive and was not reconfigured or you hold
        /// a reference to life_keeper_. The ownership is preserved within the
        /// ClientList.
203
        DataSourceClient* const dsrc_client_;
204

205
206
207
208
        /// \brief The finder for the requested zone.
        ///
        /// This is the finder corresponding to the best matching zone.
        /// This may be NULL even in case the datasrc_ is something
209
        /// else, depending on the find options.
210
        ///
211
        /// \see find
212
        const ZoneFinderPtr finder_;
213

214
215
        /// \brief If the result is an exact match.
        const bool exact_match_;
216
217
218
219
220
221

        /// \brief Something that holds the dsrc_client_ valid.
        ///
        /// As long as you hold the life_keeper_, the dsrc_client_ is
        /// guaranteed to be valid.
        const boost::shared_ptr<LifeKeeper> life_keeper_;
222
    };
223
224

    /// \brief Search for a zone through the data sources.
225
    ///
226
227
    /// This searches the contained data source clients for a one that best
    /// matches the zone name.
228
    ///
229
230
231
232
    /// There are two expected usage scenarios. One is answering queries. In
    /// this case, the zone finder is needed and the best matching superzone
    /// of the searched name is needed. Therefore, the call would look like:
    ///
233
    /// \code FindResult result(list->find(queried_name));
234
    ///   FindResult result(list->find(queried_name));
235
236
237
238
    ///   if (result.datasrc_) {
    ///       createTheAnswer(result.finder_);
    ///   } else {
    ///       createNotAuthAnswer();
239
    /// } \endcode
240
241
242
243
244
245
    ///
    /// The other scenario is manipulating zone data (XfrOut, XfrIn, DDNS,
    /// ...). In this case, the finder itself is not so important. However,
    /// we need an exact match (if we want to manipulate zone data, we must
    /// know exactly, which zone we are about to manipulate). Then the call
    ///
246
    /// \code FindResult result(list->find(zone_name, true, false));
247
    ///   FindResult result(list->find(zone_name, true, false));
248
249
250
    ///   if (result.datasrc_) {
    ///       ZoneUpdaterPtr updater(result.datasrc_->getUpdater(zone_name);
    ///       ...
251
    /// } \endcode
252
    ///
253
    /// \param zone The name of the zone to look for.
254
255
256
257
    /// \param want_exact_match If it is true, it returns only exact matches.
    ///     If the best possible match is partial, a negative result is
    ///     returned instead. It is possible the caller could check it and
    ///     act accordingly if the result would be partial match, but with this
258
    ///     set to true, the find might be actually faster under some
259
    ///     circumstances.
260
    /// \param want_finder If this is false, the finder_ member of FindResult
261
262
263
264
265
266
267
268
269
    ///     might be NULL even if the corresponding data source is found. This
    ///     is because of performance, in some cases the finder is a side
    ///     result of the searching algorithm (therefore asking for it again
    ///     would be a waste), but under other circumstances it is not, so
    ///     providing it when it is not needed would also be wasteful.
    ///
    ///     Other things are never the side effect of searching, therefore the
    ///     caller can get them explicitly (the updater, journal reader and
    ///     iterator).
270
    /// \return A FindResult describing the data source and zone with the
271
    ///     longest match against the zone parameter.
272
273
274
    virtual FindResult find(const dns::Name& zone,
                            bool want_exact_match = false,
                            bool want_finder = true) const = 0;
275
276
};

277
278
279
280
/// \brief Shared pointer to the list.
typedef boost::shared_ptr<ClientList> ClientListPtr;
/// \brief Shared const pointer to the list.
typedef boost::shared_ptr<const ClientList> ConstClientListPtr;
281

282
/// \Concrete implementation of the ClientList, which is constructed based on
283
284
285
///     configuration.
///
/// This is the implementation which is expected to be used in the servers.
286
/// However, it is expected most of the code will use it as the ClientList,
287
/// only the creation is expected to be direct.
288
289
290
///
/// While it is possible to inherit this class, it is not expected to be
/// inherited except for tests.
291
class ConfigurableClientList : public ClientList {
292
public:
293
294
295
    /// \brief Constructor
    ///
    /// \param rrclass For which class the list should work.
296
297
    ConfigurableClientList(const isc::dns::RRClass& rrclass);

298
299
300
301
302
    /// \brief Exception thrown when there's an error in configuration.
    class ConfigurationError : public Exception {
    public:
        ConfigurationError(const char* file, size_t line, const char* what) :
            Exception(file, line, what)
303
        {}
304
    };
305

306
    /// \brief Sets the configuration.
307
    ///
308
309
310
    /// This fills the ClientList with data source clients corresponding to the
    /// configuration. The data source clients are newly created or recycled
    /// from previous configuration.
311
312
313
    ///
    /// If any error is detected, an exception is thrown and the current
    /// configuration is preserved.
314
315
316
317
318
319
320
    ///
    /// \param configuration The JSON element describing the configuration to
    ///     use.
    /// \param allow_cache If it is true, the 'cache' option of the
    ///     configuration is used and some zones are cached into an In-Memory
    ///     data source according to it. If it is false, it is ignored and
    ///     no In-Memory data sources are created.
321
322
    /// \throw DataSourceError if there's a problem creating a data source
    ///     client.
323
324
    /// \throw ConfigurationError if the configuration is invalid in some
    ///     sense.
325
    /// \throw BadValue if configuration is NULL
326
327
328
329
330
    /// \throw Unexpected if something misbehaves (like the data source
    ///     returning NULL iterator).
    /// \throw NotImplemented if the auto-detection of list of zones is
    ///     needed.
    /// \throw Whatever is propagated from within the data source.
331
332
333
334
335
336
337
338
339
340
    void configure(const isc::data::ConstElementPtr& configuration,
                   bool allow_cache);

    /// \brief Returns the currently active configuration.
    ///
    /// In case configure was not called yet, it returns an empty
    /// list, which corresponds to the default content.
    const isc::data::ConstElementPtr& getConfiguration() const {
        return (configuration_);
    }
341

342
343
344
    /// \brief Result of the reload() method.
    enum ReloadResult {
        CACHE_DISABLED,     ///< The cache is not enabled in this list.
345
346
        ZONE_NOT_CACHED,    ///< Zone is served directly, not from cache.
        ZONE_NOT_FOUND,     ///< Zone does not exist or not cached.
347
348
        ZONE_SUCCESS        ///< The zone was successfully reloaded or
                            ///  the writer provided.
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    };

    /// \brief Reloads a cached zone.
    ///
    /// This method finds a zone which is loaded into a cache and reloads it.
    /// This may be used to renew the cache when the underlying data source
    /// changes.
    ///
    /// \param zone The origin of the zone to reload.
    /// \return A status if the command worked.
    /// \throw DataSourceError or anything else that the data source
    ///      containing the zone might throw is propagated.
    /// \throw DataSourceError if something unexpected happens, like when
    ///      the original data source no longer contains the cached zone.
    ReloadResult reload(const dns::Name& zone);
364

365
private:
366
367
    /// \brief Convenience type shortcut
    typedef boost::shared_ptr<memory::ZoneWriter> ZoneWriterPtr;
368
public:
369

370
371
372
373
    /// \brief Return value of getCachedZoneWriter()
    ///
    /// A pair containing status and the zone writer, for the
    /// getCachedZoneWriter() method.
374
    typedef std::pair<ReloadResult, ZoneWriterPtr> ZoneWriterPair;
375
376
377
378
379
380
381
382
383
384

    /// \brief Return a zone writer that can be used to reload a zone.
    ///
    /// This looks up a cached copy of zone and returns the ZoneWriter
    /// that can be used to reload the content of the zone. This can
    /// be used instead of reload() -- reload() works synchronously, which
    /// is not what is needed every time.
    ///
    /// \param zone The origin of the zone to reload.
    /// \return The result has two parts. The first one is a status describing
385
    ///     if it worked or not (and in case it didn't, also why). If the
386
    ///     status is ZONE_SUCCESS, the second part contains a shared pointer
387
388
    ///     to the writer. If the status is anything else, the second part is
    ///     NULL.
389
390
391
392
393
394
    /// \throw DataSourceError or anything else that the data source
    ///      containing the zone might throw is propagated.
    /// \throw DataSourceError if something unexpected happens, like when
    ///      the original data source no longer contains the cached zone.
    ZoneWriterPair getCachedZoneWriter(const dns::Name& zone);

395
    /// \brief Implementation of the ClientList::find.
396
    virtual FindResult find(const dns::Name& zone,
397
398
                            bool want_exact_match = false,
                            bool want_finder = true) const;
399

400
    /// \brief This holds one data source client and corresponding information.
401
402
    ///
    /// \todo The content yet to be defined.
403
    struct DataSourceInfo {
404
        // Plays a role of default constructor too (for vector)
405
        DataSourceInfo(const dns::RRClass& rrclass,
406
                       const boost::shared_ptr
407
408
                           <isc::datasrc::memory::ZoneTableSegment>&
                               ztable_segment,
409
410
                       bool has_cache = false,
                       const std::string& name = std::string());
411
        DataSourceInfo(DataSourceClient* data_src_client,
412
                       const DataSourceClientContainerPtr& container,
413
                       bool has_cache, const dns::RRClass& rrclass,
414
                       const boost::shared_ptr
415
                           <isc::datasrc::memory::ZoneTableSegment>&
416
                               ztable_segment, const std::string& name);
417
        DataSourceClient* data_src_client_;
418
        DataSourceClientContainerPtr container_;
419
420
421
422
423
424
425

        // Accessor to cache_ in the form of DataSourceClient, hiding
        // the existence of InMemoryClient as much as possible.  We should
        // really consider cleaner abstraction, but for now it works.
        // This is also only intended to be used in auth unit tests right now.
        // No other applications or tests may use it.
        const DataSourceClient* getCacheClient() const;
426
        boost::shared_ptr<memory::InMemoryClient> cache_;
427
        boost::shared_ptr<memory::ZoneTableSegment> ztable_segment_;
428
        std::string name_;
429
    };
430

431
432
    /// \brief The collection of data sources.
    typedef std::vector<DataSourceInfo> DataSources;
433

434
435
436
437
438
    /// \brief Convenience type alias.
    ///
    /// \see getDataSource
    typedef std::pair<DataSourceClient*, DataSourceClientContainerPtr>
        DataSourcePair;
439

440
    /// \brief Create a data source client of given type and configuration.
441
442
443
444
    ///
    /// This is a thin wrapper around the DataSourceClientContainer
    /// constructor. The function is here to make it possible for tests
    /// to replace the DataSourceClientContainer with something else.
445
446
    /// Also, derived classes could want to create the data source clients
    /// in a different way, though inheriting this class is not recommended.
447
448
    ///
    /// The parameters are the same as of the constructor.
449
    /// \return Pair containing both the data source client and the container.
450
    ///     The container might be NULL in the derived class, it is
451
    ///     only stored so the data source client is properly destroyed when
452
    ///     not needed. However, in such case, it is the caller's
453
    ///     responsibility to ensure the data source client is deleted when
454
    ///     needed.
455
456
457
    virtual DataSourcePair getDataSourceClient(const std::string& type,
                                               const data::ConstElementPtr&
                                               configuration);
458
459
460
461
462

    /// \brief Get status information of all internal data sources.
    ///
    /// Get a DataSourceStatus for current state of each data source client
    /// in this list.
463
464
465
    ///
    /// This may throw standad exceptions, such as std::bad_alloc. Otherwise,
    /// it is exception free.
466
    std::vector<DataSourceStatus> getStatus() const;
467
public:
468
    /// \brief Access to the data source clients.
469
    ///
470
471
472
473
    /// It can be used to examine the loaded list of data sources clients
    /// directly. It is not known if it is of any use other than testing, but
    /// it might be, so it is just made public (there's no real reason to
    /// hide it).
474
    const DataSources& getDataSources() const { return (data_sources_); }
475
private:
476
477
478
479
480
481
482
483
    struct MutableResult;
    /// \brief Internal implementation of find.
    ///
    /// The class itself needs to do some internal searches in other methods,
    /// so the implementation is shared.
    ///
    /// The result is returned as parameter because MutableResult is not
    /// defined in the header file.
484
485
486
487
    ///
    /// If there's no match, the result is not modified. Therefore, this
    /// expects to get a fresh result object each time it is called, not
    /// to reuse it.
488
489
    void findInternal(MutableResult& result, const dns::Name& name,
                      bool want_exact_match, bool want_finder) const;
490
    const isc::dns::RRClass rrclass_;
491

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
492
493
    /// \brief Currently active configuration.
    isc::data::ConstElementPtr configuration_;
494

495
496
    /// \brief The last set value of allow_cache.
    bool allow_cache_;
497
498
499
500
501
502
503
504

protected:
    /// \brief The data sources held here.
    ///
    /// All our data sources are stored here. It is protected to let the
    /// tests in. You should consider it private if you ever want to
    /// derive this class (which is not really recommended anyway).
    DataSources data_sources_;
505
506
};

507
508
509
/// \brief Shortcut typedef for maps of client_lists.
typedef boost::shared_ptr<std::map<
    isc::dns::RRClass, boost::shared_ptr<ConfigurableClientList> > >
510
        ClientListMapPtr;
511

512
513
514
515
} // namespace datasrc
} // namespace isc

#endif // DATASRC_CONTAINER_H