client.h 20.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2011  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.

15
16
#ifndef DATA_SOURCE_CLIENT_H
#define DATA_SOURCE_CLIENT_H 1
17

18
19
#include <utility>

20
#include <boost/noncopyable.hpp>
21
22
#include <boost/shared_ptr.hpp>

23
24
#include <datasrc/zone.h>

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
/// \file
/// Datasource clients
///
/// The data source client API is specified in client.h, and provides the
/// functionality to query and modify data in the data sources. There are
/// multiple datasource implementations, and by subclassing DataSourceClient or
/// DatabaseClient, more can be added.
///
/// All datasources are implemented as loadable modules, with a name of the
/// form "<type>_ds.so". This has been chosen intentionally, to minimize
/// confusion and potential mistakes.
///
/// In order to use a datasource client backend, the class
/// DataSourceClientContainer is provided in factory.h; this will load the
/// library, set up the instance, and clean everything up once it is destroyed.
///
/// Access to the actual instance is provided with the getInstance() method
/// in DataSourceClientContainer
///
/// \note Depending on actual usage, we might consider making the container
/// a transparent abstraction layer, so it can be used as a DataSourceClient
/// directly. This has some other implications though so for now the only access
/// provided is through getInstance()).
///
/// For datasource backends, we use a dynamically loaded library system (with
/// dlopen()). This library must contain the following things;
/// - A subclass of DataSourceClient or DatabaseClient (which itself is a
///   subclass of DataSourceClient)
/// - A creator function for an instance of that subclass, of the form:
/// \code
/// extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr cfg);
/// \endcode
/// - A destructor for said instance, of the form:
/// \code
/// extern "C" void destroyInstance(isc::data::DataSourceClient* instance);
/// \endcode
///
/// See the documentation for the \link DataSourceClient \endlink class for
/// more information on implementing subclasses of it.
///

66
67
68
namespace isc {
namespace datasrc {

69
// The iterator.h is not included on purpose, most application won't need it
70
71
72
class ZoneIterator;
typedef boost::shared_ptr<ZoneIterator> ZoneIteratorPtr;

73
/// \brief The base class of data source clients.
74
///
75
76
77
78
79
80
81
/// This is an abstract base class that defines the common interface for
/// various types of data source clients.  A data source client is a top level
/// access point to a data source, allowing various operations on the data
/// source such as lookups, traversing or updates.  The client class itself
/// has limited focus and delegates the responsibility for these specific
/// operations to other classes; in general methods of this class act as
/// factories of these other classes.
82
///
83
84
85
/// See \link datasrc/client.h datasrc/client.h \endlink for more information
/// on adding datasource implementations.
///
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
/// The following derived classes are currently (expected to be) provided:
/// - \c InMemoryClient: A client of a conceptual data source that stores
/// all necessary data in memory for faster lookups
/// - \c DatabaseClient: A client that uses a real database backend (such as
/// an SQL database).  It would internally hold a connection to the underlying
/// database system.
///
/// \note It is intentional that while the term these derived classes don't
/// contain "DataSource" unlike their base class.  It's also noteworthy
/// that the naming of the base class is somewhat redundant because the
/// namespace \c datasrc would indicate that it's related to a data source.
/// The redundant naming comes from the observation that namespaces are
/// often omitted with \c using directives, in which case "Client"
/// would be too generic.  On the other hand, concrete derived classes are
/// generally not expected to be referenced directly from other modules and
/// applications, so we'll give them more concise names such as InMemoryClient.
///
/// A single \c DataSourceClient object is expected to handle only a single
/// RR class even if the underlying data source contains records for multiple
/// RR classes.  Likewise, (when we support views) a \c DataSourceClient
/// object is expected to handle only a single view.
///
/// If the application uses multiple threads, each thread will need to
/// create and use a separate DataSourceClient.  This is because some
/// database backend doesn't allow multiple threads to share the same
/// connection to the database.
///
/// \note For a client using an in memory backend, this may result in
/// having a multiple copies of the same data in memory, increasing the
/// memory footprint substantially.  Depending on how to support multiple
/// CPU cores for concurrent lookups on the same single data source (which
/// is not fully fixed yet, and for which multiple threads may be used),
/// this design may have to be revisited.
///
/// This class (and therefore its derived classes) are not copyable.
/// This is because the derived classes would generally contain attributes
/// that are not easy to copy (such as a large size of in memory data or a
/// network connection to a database server).  In order to avoid a surprising
/// disruption with a naive copy it's prohibited explicitly.  For the expected
/// usage of the client classes the restriction should be acceptable.
126
///
127
128
/// \todo This class is still not complete. It will need more factory methods,
/// e.g. for (re)loading a zone.
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
class DataSourceClient : boost::noncopyable {
public:
    /// \brief A helper structure to represent the search result of
    /// \c find().
    ///
    /// This is a straightforward pair of the result code and a share pointer
    /// to the found zone to represent the result of \c find().
    /// We use this in order to avoid overloading the return value for both
    /// the result code ("success" or "not found") and the found object,
    /// i.e., avoid using \c NULL to mean "not found", etc.
    ///
    /// This is a simple value class with no internal state, so for
    /// convenience we allow the applications to refer to the members
    /// directly.
    ///
    /// See the description of \c find() for the semantics of the member
    /// variables.
    struct FindResult {
        FindResult(result::Result param_code,
                   const ZoneFinderPtr param_zone_finder) :
            code(param_code), zone_finder(param_zone_finder)
        {}
        const result::Result code;
        const ZoneFinderPtr zone_finder;
    };

    ///
    /// \name Constructors and Destructor.
    ///
protected:
    /// Default constructor.
    ///
161
162
163
164
165
166
167
    /// This is intentionally defined as protected as this base class
    /// should never be instantiated directly.
    ///
    /// The constructor of a concrete derived class may throw an exception.
    /// This interface does not specify which exceptions can happen (at least
    /// at this moment), and the caller should expect any type of exception
    /// and react accordingly.
168
169
170
171
172
173
174
    DataSourceClient() {}

public:
    /// The destructor.
    virtual ~DataSourceClient() {}
    //@}

175
    /// Returns a \c ZoneFinder for a zone that best matches the given name.
176
    ///
177
178
179
180
    /// A concrete derived version of this method gets access to its backend
    /// data source to search for a zone whose origin gives the longest match
    /// against \c name.  It returns the search result in the form of a
    /// \c FindResult object as follows:
181
    /// - \c code: The result code of the operation.
182
    ///   - \c result::SUCCESS: A zone that gives an exact match is found
183
    ///   - \c result::PARTIALMATCH: A zone whose origin is a
184
    ///   super domain of \c name is found (but there is no exact match)
185
    ///   - \c result::NOTFOUND: For all other cases.
186
187
    /// - \c zone_finder: Pointer to a \c ZoneFinder object for the found zone
    /// if one is found; otherwise \c NULL.
188
    ///
189
190
191
192
    /// A specific derived version of this method may throw an exception.
    /// This interface does not specify which exceptions can happen (at least
    /// at this moment), and the caller should expect any type of exception
    /// and react accordingly.
193
194
195
196
    ///
    /// \param name A domain name for which the search is performed.
    /// \return A \c FindResult object enclosing the search result (see above).
    virtual FindResult findZone(const isc::dns::Name& name) const = 0;
Jelte Jansen's avatar
Jelte Jansen committed
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
    /// \brief Returns an iterator to the given zone
    ///
    /// This allows for traversing the whole zone. The returned object can
    /// provide the RRsets one by one.
    ///
    /// This throws DataSourceError when the zone does not exist in the
    /// datasource.
    ///
    /// The default implementation throws isc::NotImplemented. This allows
    /// for easy and fast deployment of minimal custom data sources, where
    /// the user/implementator doesn't have to care about anything else but
    /// the actual queries. Also, in some cases, it isn't possible to traverse
    /// the zone from logic point of view (eg. dynamically generated zone
    /// data).
    ///
    /// It is not fixed if a concrete implementation of this method can throw
    /// anything else.
    ///
    /// \param name The name of zone apex to be traversed. It doesn't do
    ///     nearest match as findZone.
218
219
220
221
222
223
    /// \param separate_rrs If true, the iterator will return each RR as a
    ///                     new RRset object. If false, the iterator will
    ///                     combine consecutive RRs with the name and type
    ///                     into 1 RRset. The capitalization of the RRset will
    ///                     be that of the first RR read, and TTLs will be
    ///                     adjusted to the lowest one found.
224
    /// \return Pointer to the iterator.
225
    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
226
                                        bool separate_rrs = false) const;
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
    /// Return an updater to make updates to a specific zone.
    ///
    /// The RR class of the zone is the one that the client is expected to
    /// handle (see the detailed description of this class).
    ///
    /// If the specified zone is not found via the client, a NULL pointer
    /// will be returned; in other words a completely new zone cannot be
    /// created using an updater.  It must be created beforehand (even if
    /// it's an empty placeholder) in a way specific to the underlying data
    /// source.
    ///
    /// Conceptually, the updater will trigger a separate transaction for
    /// subsequent updates to the zone within the context of the updater
    /// (the actual implementation of the "transaction" may vary for the
    /// specific underlying data source).  Until \c commit() is performed
    /// on the updater, the intermediate updates won't affect the results
    /// of other methods (and the result of the object's methods created
    /// by other factory methods).  Likewise, if the updater is destructed
    /// without performing \c commit(), the intermediate updates will be
    /// effectively canceled and will never affect other methods.
    ///
    /// If the underlying data source allows concurrent updates, this method
    /// can be called multiple times while the previously returned updater(s)
    /// are still active.  In this case each updater triggers a different
    /// "transaction".  Normally it would be for different zones for such a
    /// case as handling multiple incoming AXFR streams concurrently, but
    /// this interface does not even prohibit an attempt of getting more than
    /// one updater for the same zone, as long as the underlying data source
    /// allows such an operation (and any conflict resolution is left to the
    /// specific derived class implementation).
    ///
    /// If \c replace is true, any existing RRs of the zone will be
    /// deleted on successful completion of updates (after \c commit() on
    /// the updater); if it's false, the existing RRs will be
    /// intact unless explicitly deleted by \c deleteRRset() on the updater.
    ///
    /// A data source can be "read only" or can prohibit partial updates.
    /// In such cases this method will result in an \c isc::NotImplemented
    /// exception unconditionally or when \c replace is false).
    ///
268
269
270
271
272
    /// If \c journaling is true, the data source should store a journal
    /// of changes. These can be used later on by, for example, IXFR-out.
    /// However, the parameter is a hint only. It might be unable to store
    /// them and they would be silently discarded. Or it might need to
    /// store them no matter what (for example a git-based data source would
273
274
    /// store journal implicitly). When the \c journaling is true, it
    /// requires that the following update be formatted as IXFR transfer
275
    /// (SOA to be removed, bunch of RRs to be removed, SOA to be added,
276
277
278
    /// bunch of RRs to be added, and possibly repeated). However, it is not
    /// required that the updater checks that. If it is false, it must not
    /// require so and must accept any order of changes.
279
280
281
282
283
    ///
    /// We don't support erasing the whole zone (by replace being true) and
    /// saving a journal at the same time. In such situation, BadValue is
    /// thrown.
    ///
284
    /// \note To avoid throwing the exception accidentally with a lazy
285
    /// implementation, we still keep this method pure virtual without
286
287
288
289
290
291
292
293
    /// an implementation.  All derived classes must explicitly define this
    /// method, even if it simply throws the NotImplemented exception.
    ///
    /// \exception NotImplemented The underlying data source does not support
    /// updates.
    /// \exception DataSourceError Internal error in the underlying data
    /// source.
    /// \exception std::bad_alloc Resource allocation failure.
294
    /// \exception BadValue if both replace and journaling are true.
295
296
297
    ///
    /// \param name The zone name to be updated
    /// \param replace Whether to delete existing RRs before making updates
298
    /// \param journaling The zone updater should store a journal of the
299
    ///     changes.
300
301
302
303
    ///
    /// \return A pointer to the updater; it will be NULL if the specified
    /// zone isn't found.
    virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name,
304
305
                                      bool replace, bool journaling = false)
        const = 0;
306

307
308
309
310
311
312
313
314
315
316
317
318
319
    /// Return a journal reader to retrieve differences of a zone.
    ///
    /// A derived version of this method creates a concrete
    /// \c ZoneJournalReader object specific to the underlying data source
    /// for the specified name of zone and differences between the versions
    /// specified by the beginning and ending serials of the corresponding
    /// SOA RRs.
    /// The RR class of the zone is the one that the client is expected to
    /// handle (see the detailed description of this class).
    ///
    /// Note that the SOA serials are compared by the semantics of the serial
    /// number arithmetic.  So, for example, \c begin_serial can be larger than
    /// \c end_serial as bare unsigned integers.  The underlying data source
JINMEI Tatuya's avatar
JINMEI Tatuya committed
320
    /// implementation is assumed to keep track of sufficient history to
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
    /// identify (if exist) the corresponding difference between the specified
    /// versions.
    ///
    /// This method returns the result as a pair of a result code and
    /// a pointer to a \c ZoneJournalReader object.  On success, the result
    /// code is \c SUCCESS and the pointer must be non NULL; otherwise
    /// the result code is something other than \c SUCCESS and the pinter
    /// must be NULL.
    ///
    /// If the specified zone is not found in the data source, the result
    /// code is \c NO_SUCH_ZONE.
    /// Otherwise, if specified range of difference for the zone is not found
    /// in the data source, the result code is \c NO_SUCH_VERSION.
    ///
    /// Handling differences is an optional feature of data source.
    /// If the underlying data source does not support difference handling,
    /// this method for that type of data source can throw an exception of
    /// class \c NotImplemented.
    ///
    /// \exception NotImplemented The data source does not support differences.
    /// \exception DataSourceError Other operational errors at the data source
    /// level.
    ///
    /// \param zone The name of the zone for which the difference should be
    /// retrieved.
    /// \param begin_serial The SOA serial of the beginning version of the
    /// differences.
    /// \param end_serial The SOA serial of the ending version of the
    /// differences.
    ///
    /// \return A pair of result code and a pointer to \c ZoneJournalReader.
352
    virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
353
354
    getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
                     uint32_t end_serial) const = 0;
355
356
357
358
359
360

    /// Return the number of zones currently known to this datasource
    ///
    /// This is an optional convenience method, currently only implemented
    /// by the InMemory datasource. By default, it throws NotImplemented
    ///
361
362
363
364
    /// \note This is a tentative API, and this method is likely to change
    /// or be removed in the near future. For that reason, it currently
    /// provides a default implementation that throws NotImplemented.
    ///
365
366
367
368
    /// \exception NotImplemented Thrown if this method is not supported
    ///            by the datasource
    ///
    /// \return The number of zones known to this datasource
369
    virtual unsigned int getZoneCount() const;
370

371
    /// \brief Create a zone in the data source
372
373
374
375
376
    ///
    /// Creates a new (empty) zone in the data source backend, which
    /// can subsequently be filled with data (through getUpdater()).
    ///
    /// \note This is a tentative API, and this method is likely to change
377
378
    /// or be removed in the near future. For that reason, it currently
    /// provides a default implementation that throws NotImplemented.
379
380
381
382
383
384
385
386
387
    ///
    /// Apart from the two exceptions mentioned below, in theory this
    /// call can throw anything, depending on the implementation of
    /// the datasource backend.
    ///
    /// \throw NotImplemented If the datasource backend does not support
    ///                       direct zone creation.
    /// \throw DataSourceError If something goes wrong in the data source
    ///                        while creating the zone.
388
    /// \param zone_name The (fully qualified) name of the zone to create
389
    /// \return True if the zone was added, false if it already existed
390
391
    virtual bool createZone(const dns::Name& zone_name);

JINMEI Tatuya's avatar
JINMEI Tatuya committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
    /// \brief Delete a zone from the data source
    ///
    /// This method also checks if the specified zone exists in the data
    /// source, and returns true/false depending on whether the zone
    /// existed/not existed, respectively.  In either case, on successful
    /// return it ensures the data source does not contain the specified
    /// name of the zone.
    ///
    /// \note This is a tentative API, and this method is likely to change
    /// or be removed in the near future. For that reason, it currently
    /// provides a default implementation that throws NotImplemented.
    /// Note also that this method does not delete other database records
    /// related to the zone, such as zone's resource records or differences
    /// corresponding to updates made in the zone.  This is primarily for
    /// implementation simplicity (in the currently intended usage there
    /// wouldn't be such other data at the time of this call anyway) and due
    /// to the fact that details of managing zones is still in flux.  Once
    /// the design in this area is fixed we may revisit the behavior.
    ///
    /// Apart from the two exceptions mentioned below, in theory this
    /// call can throw anything, depending on the implementation of
    /// the datasource backend.
    ///
    /// \throw NotImplemented If the datasource backend does not support
    ///                       direct zone deletion.
    /// \throw DataSourceError If something goes wrong in the data source
    ///                        while deleting the zone.
    /// \param zone_name The (fully qualified) name of the zone to be deleted
    /// \return true if the zone previously existed and has been deleted by
    /// this method; false if the zone didn't exist.
422
    virtual bool deleteZone(const dns::Name& zone_name);
423
424
425
426
427
428
429
};
}
}
#endif  // DATA_SOURCE_CLIENT_H
// Local Variables:
// mode: c++
// End: