Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #ifndef __DATA_SOURCE_CLIENT_H
16 : : #define __DATA_SOURCE_CLIENT_H 1
17 : :
18 : : #include <utility>
19 : :
20 : : #include <boost/noncopyable.hpp>
21 : : #include <boost/shared_ptr.hpp>
22 : :
23 : : #include <exceptions/exceptions.h>
24 : :
25 : : #include <datasrc/zone.h>
26 : :
27 : : /// \file
28 : : /// Datasource clients
29 : : ///
30 : : /// The data source client API is specified in client.h, and provides the
31 : : /// functionality to query and modify data in the data sources. There are
32 : : /// multiple datasource implementations, and by subclassing DataSourceClient or
33 : : /// DatabaseClient, more can be added.
34 : : ///
35 : : /// All datasources are implemented as loadable modules, with a name of the
36 : : /// form "<type>_ds.so". This has been chosen intentionally, to minimize
37 : : /// confusion and potential mistakes.
38 : : ///
39 : : /// In order to use a datasource client backend, the class
40 : : /// DataSourceClientContainer is provided in factory.h; this will load the
41 : : /// library, set up the instance, and clean everything up once it is destroyed.
42 : : ///
43 : : /// Access to the actual instance is provided with the getInstance() method
44 : : /// in DataSourceClientContainer
45 : : ///
46 : : /// \note Depending on actual usage, we might consider making the container
47 : : /// a transparent abstraction layer, so it can be used as a DataSourceClient
48 : : /// directly. This has some other implications though so for now the only access
49 : : /// provided is through getInstance()).
50 : : ///
51 : : /// For datasource backends, we use a dynamically loaded library system (with
52 : : /// dlopen()). This library must contain the following things;
53 : : /// - A subclass of DataSourceClient or DatabaseClient (which itself is a
54 : : /// subclass of DataSourceClient)
55 : : /// - A creator function for an instance of that subclass, of the form:
56 : : /// \code
57 : : /// extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr cfg);
58 : : /// \endcode
59 : : /// - A destructor for said instance, of the form:
60 : : /// \code
61 : : /// extern "C" void destroyInstance(isc::data::DataSourceClient* instance);
62 : : /// \endcode
63 : : ///
64 : : /// See the documentation for the \link DataSourceClient \endlink class for
65 : : /// more information on implementing subclasses of it.
66 : : ///
67 : :
68 : : namespace isc {
69 : : namespace datasrc {
70 : :
71 : : // The iterator.h is not included on purpose, most application won't need it
72 : : class ZoneIterator;
73 : : typedef boost::shared_ptr<ZoneIterator> ZoneIteratorPtr;
74 : :
75 : : /// \brief The base class of data source clients.
76 : : ///
77 : : /// This is an abstract base class that defines the common interface for
78 : : /// various types of data source clients. A data source client is a top level
79 : : /// access point to a data source, allowing various operations on the data
80 : : /// source such as lookups, traversing or updates. The client class itself
81 : : /// has limited focus and delegates the responsibility for these specific
82 : : /// operations to other classes; in general methods of this class act as
83 : : /// factories of these other classes.
84 : : ///
85 : : /// See \link datasrc/client.h datasrc/client.h \endlink for more information
86 : : /// on adding datasource implementations.
87 : : ///
88 : : /// The following derived classes are currently (expected to be) provided:
89 : : /// - \c InMemoryClient: A client of a conceptual data source that stores
90 : : /// all necessary data in memory for faster lookups
91 : : /// - \c DatabaseClient: A client that uses a real database backend (such as
92 : : /// an SQL database). It would internally hold a connection to the underlying
93 : : /// database system.
94 : : ///
95 : : /// \note It is intentional that while the term these derived classes don't
96 : : /// contain "DataSource" unlike their base class. It's also noteworthy
97 : : /// that the naming of the base class is somewhat redundant because the
98 : : /// namespace \c datasrc would indicate that it's related to a data source.
99 : : /// The redundant naming comes from the observation that namespaces are
100 : : /// often omitted with \c using directives, in which case "Client"
101 : : /// would be too generic. On the other hand, concrete derived classes are
102 : : /// generally not expected to be referenced directly from other modules and
103 : : /// applications, so we'll give them more concise names such as InMemoryClient.
104 : : ///
105 : : /// A single \c DataSourceClient object is expected to handle only a single
106 : : /// RR class even if the underlying data source contains records for multiple
107 : : /// RR classes. Likewise, (when we support views) a \c DataSourceClient
108 : : /// object is expected to handle only a single view.
109 : : ///
110 : : /// If the application uses multiple threads, each thread will need to
111 : : /// create and use a separate DataSourceClient. This is because some
112 : : /// database backend doesn't allow multiple threads to share the same
113 : : /// connection to the database.
114 : : ///
115 : : /// \note For a client using an in memory backend, this may result in
116 : : /// having a multiple copies of the same data in memory, increasing the
117 : : /// memory footprint substantially. Depending on how to support multiple
118 : : /// CPU cores for concurrent lookups on the same single data source (which
119 : : /// is not fully fixed yet, and for which multiple threads may be used),
120 : : /// this design may have to be revisited.
121 : : ///
122 : : /// This class (and therefore its derived classes) are not copyable.
123 : : /// This is because the derived classes would generally contain attributes
124 : : /// that are not easy to copy (such as a large size of in memory data or a
125 : : /// network connection to a database server). In order to avoid a surprising
126 : : /// disruption with a naive copy it's prohibited explicitly. For the expected
127 : : /// usage of the client classes the restriction should be acceptable.
128 : : ///
129 : : /// \todo This class is still not complete. It will need more factory methods,
130 : : /// e.g. for (re)loading a zone.
131 : : class DataSourceClient : boost::noncopyable {
132 : : public:
133 : : /// \brief A helper structure to represent the search result of
134 : : /// \c find().
135 : : ///
136 : : /// This is a straightforward pair of the result code and a share pointer
137 : : /// to the found zone to represent the result of \c find().
138 : : /// We use this in order to avoid overloading the return value for both
139 : : /// the result code ("success" or "not found") and the found object,
140 : : /// i.e., avoid using \c NULL to mean "not found", etc.
141 : : ///
142 : : /// This is a simple value class with no internal state, so for
143 : : /// convenience we allow the applications to refer to the members
144 : : /// directly.
145 : : ///
146 : : /// See the description of \c find() for the semantics of the member
147 : : /// variables.
148 : 569 : struct FindResult {
149 : : FindResult(result::Result param_code,
150 : : const ZoneFinderPtr param_zone_finder) :
151 : 569 : code(param_code), zone_finder(param_zone_finder)
152 : : {}
153 : : const result::Result code;
154 : : const ZoneFinderPtr zone_finder;
155 : : };
156 : :
157 : : ///
158 : : /// \name Constructors and Destructor.
159 : : ///
160 : : protected:
161 : : /// Default constructor.
162 : : ///
163 : : /// This is intentionally defined as protected as this base class
164 : : /// should never be instantiated directly.
165 : : ///
166 : : /// The constructor of a concrete derived class may throw an exception.
167 : : /// This interface does not specify which exceptions can happen (at least
168 : : /// at this moment), and the caller should expect any type of exception
169 : : /// and react accordingly.
170 : 406 : DataSourceClient() {}
171 : :
172 : : public:
173 : : /// The destructor.
174 : 406 : virtual ~DataSourceClient() {}
175 : : //@}
176 : :
177 : : /// Returns a \c ZoneFinder for a zone that best matches the given name.
178 : : ///
179 : : /// A concrete derived version of this method gets access to its backend
180 : : /// data source to search for a zone whose origin gives the longest match
181 : : /// against \c name. It returns the search result in the form of a
182 : : /// \c FindResult object as follows:
183 : : /// - \c code: The result code of the operation.
184 : : /// - \c result::SUCCESS: A zone that gives an exact match is found
185 : : /// - \c result::PARTIALMATCH: A zone whose origin is a
186 : : /// super domain of \c name is found (but there is no exact match)
187 : : /// - \c result::NOTFOUND: For all other cases.
188 : : /// - \c zone_finder: Pointer to a \c ZoneFinder object for the found zone
189 : : /// if one is found; otherwise \c NULL.
190 : : ///
191 : : /// A specific derived version of this method may throw an exception.
192 : : /// This interface does not specify which exceptions can happen (at least
193 : : /// at this moment), and the caller should expect any type of exception
194 : : /// and react accordingly.
195 : : ///
196 : : /// \param name A domain name for which the search is performed.
197 : : /// \return A \c FindResult object enclosing the search result (see above).
198 : : virtual FindResult findZone(const isc::dns::Name& name) const = 0;
199 : :
200 : : /// \brief Returns an iterator to the given zone
201 : : ///
202 : : /// This allows for traversing the whole zone. The returned object can
203 : : /// provide the RRsets one by one.
204 : : ///
205 : : /// This throws DataSourceError when the zone does not exist in the
206 : : /// datasource.
207 : : ///
208 : : /// The default implementation throws isc::NotImplemented. This allows
209 : : /// for easy and fast deployment of minimal custom data sources, where
210 : : /// the user/implementator doesn't have to care about anything else but
211 : : /// the actual queries. Also, in some cases, it isn't possible to traverse
212 : : /// the zone from logic point of view (eg. dynamically generated zone
213 : : /// data).
214 : : ///
215 : : /// It is not fixed if a concrete implementation of this method can throw
216 : : /// anything else.
217 : : ///
218 : : /// \param name The name of zone apex to be traversed. It doesn't do
219 : : /// nearest match as findZone.
220 : : /// \param separate_rrs If true, the iterator will return each RR as a
221 : : /// new RRset object. If false, the iterator will
222 : : /// combine consecutive RRs with the name and type
223 : : /// into 1 RRset. The capitalization of the RRset will
224 : : /// be that of the first RR read, and TTLs will be
225 : : /// adjusted to the lowest one found.
226 : : /// \return Pointer to the iterator.
227 : 1 : virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
228 : : bool separate_rrs = false) const {
229 : : // This is here to both document the parameter in doxygen (therefore it
230 : : // needs a name) and avoid unused parameter warning.
231 : : static_cast<void>(name);
232 : : static_cast<void>(separate_rrs);
233 : :
234 [ + - ][ + - ]: 2 : isc_throw(isc::NotImplemented,
235 : : "Data source doesn't support iteration");
236 : : }
237 : :
238 : : /// Return an updater to make updates to a specific zone.
239 : : ///
240 : : /// The RR class of the zone is the one that the client is expected to
241 : : /// handle (see the detailed description of this class).
242 : : ///
243 : : /// If the specified zone is not found via the client, a NULL pointer
244 : : /// will be returned; in other words a completely new zone cannot be
245 : : /// created using an updater. It must be created beforehand (even if
246 : : /// it's an empty placeholder) in a way specific to the underlying data
247 : : /// source.
248 : : ///
249 : : /// Conceptually, the updater will trigger a separate transaction for
250 : : /// subsequent updates to the zone within the context of the updater
251 : : /// (the actual implementation of the "transaction" may vary for the
252 : : /// specific underlying data source). Until \c commit() is performed
253 : : /// on the updater, the intermediate updates won't affect the results
254 : : /// of other methods (and the result of the object's methods created
255 : : /// by other factory methods). Likewise, if the updater is destructed
256 : : /// without performing \c commit(), the intermediate updates will be
257 : : /// effectively canceled and will never affect other methods.
258 : : ///
259 : : /// If the underlying data source allows concurrent updates, this method
260 : : /// can be called multiple times while the previously returned updater(s)
261 : : /// are still active. In this case each updater triggers a different
262 : : /// "transaction". Normally it would be for different zones for such a
263 : : /// case as handling multiple incoming AXFR streams concurrently, but
264 : : /// this interface does not even prohibit an attempt of getting more than
265 : : /// one updater for the same zone, as long as the underlying data source
266 : : /// allows such an operation (and any conflict resolution is left to the
267 : : /// specific derived class implementation).
268 : : ///
269 : : /// If \c replace is true, any existing RRs of the zone will be
270 : : /// deleted on successful completion of updates (after \c commit() on
271 : : /// the updater); if it's false, the existing RRs will be
272 : : /// intact unless explicitly deleted by \c deleteRRset() on the updater.
273 : : ///
274 : : /// A data source can be "read only" or can prohibit partial updates.
275 : : /// In such cases this method will result in an \c isc::NotImplemented
276 : : /// exception unconditionally or when \c replace is false).
277 : : ///
278 : : /// If \c journaling is true, the data source should store a journal
279 : : /// of changes. These can be used later on by, for example, IXFR-out.
280 : : /// However, the parameter is a hint only. It might be unable to store
281 : : /// them and they would be silently discarded. Or it might need to
282 : : /// store them no matter what (for example a git-based data source would
283 : : /// store journal implicitly). When the \c journaling is true, it
284 : : /// requires that the following update be formatted as IXFR transfer
285 : : /// (SOA to be removed, bunch of RRs to be removed, SOA to be added,
286 : : /// bunch of RRs to be added, and possibly repeated). However, it is not
287 : : /// required that the updater checks that. If it is false, it must not
288 : : /// require so and must accept any order of changes.
289 : : ///
290 : : /// We don't support erasing the whole zone (by replace being true) and
291 : : /// saving a journal at the same time. In such situation, BadValue is
292 : : /// thrown.
293 : : ///
294 : : /// \note To avoid throwing the exception accidentally with a lazy
295 : : /// implementation, we still keep this method pure virtual without
296 : : /// an implementation. All derived classes must explicitly define this
297 : : /// method, even if it simply throws the NotImplemented exception.
298 : : ///
299 : : /// \exception NotImplemented The underlying data source does not support
300 : : /// updates.
301 : : /// \exception DataSourceError Internal error in the underlying data
302 : : /// source.
303 : : /// \exception std::bad_alloc Resource allocation failure.
304 : : /// \exception BadValue if both replace and journaling are true.
305 : : ///
306 : : /// \param name The zone name to be updated
307 : : /// \param replace Whether to delete existing RRs before making updates
308 : : /// \param journaling The zone updater should store a journal of the
309 : : /// changes.
310 : : ///
311 : : /// \return A pointer to the updater; it will be NULL if the specified
312 : : /// zone isn't found.
313 : : virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name,
314 : : bool replace, bool journaling = false)
315 : : const = 0;
316 : :
317 : : /// Return a journal reader to retrieve differences of a zone.
318 : : ///
319 : : /// A derived version of this method creates a concrete
320 : : /// \c ZoneJournalReader object specific to the underlying data source
321 : : /// for the specified name of zone and differences between the versions
322 : : /// specified by the beginning and ending serials of the corresponding
323 : : /// SOA RRs.
324 : : /// The RR class of the zone is the one that the client is expected to
325 : : /// handle (see the detailed description of this class).
326 : : ///
327 : : /// Note that the SOA serials are compared by the semantics of the serial
328 : : /// number arithmetic. So, for example, \c begin_serial can be larger than
329 : : /// \c end_serial as bare unsigned integers. The underlying data source
330 : : /// implementation is assumed to keep track of sufficient history to
331 : : /// identify (if exist) the corresponding difference between the specified
332 : : /// versions.
333 : : ///
334 : : /// This method returns the result as a pair of a result code and
335 : : /// a pointer to a \c ZoneJournalReader object. On success, the result
336 : : /// code is \c SUCCESS and the pointer must be non NULL; otherwise
337 : : /// the result code is something other than \c SUCCESS and the pinter
338 : : /// must be NULL.
339 : : ///
340 : : /// If the specified zone is not found in the data source, the result
341 : : /// code is \c NO_SUCH_ZONE.
342 : : /// Otherwise, if specified range of difference for the zone is not found
343 : : /// in the data source, the result code is \c NO_SUCH_VERSION.
344 : : ///
345 : : /// Handling differences is an optional feature of data source.
346 : : /// If the underlying data source does not support difference handling,
347 : : /// this method for that type of data source can throw an exception of
348 : : /// class \c NotImplemented.
349 : : ///
350 : : /// \exception NotImplemented The data source does not support differences.
351 : : /// \exception DataSourceError Other operational errors at the data source
352 : : /// level.
353 : : ///
354 : : /// \param zone The name of the zone for which the difference should be
355 : : /// retrieved.
356 : : /// \param begin_serial The SOA serial of the beginning version of the
357 : : /// differences.
358 : : /// \param end_serial The SOA serial of the ending version of the
359 : : /// differences.
360 : : ///
361 : : /// \return A pair of result code and a pointer to \c ZoneJournalReader.
362 : : virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
363 : : getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
364 : : uint32_t end_serial) const = 0;
365 : : };
366 : : }
367 : : }
368 : : #endif // DATA_SOURCE_CLIENT_H
369 : : // Local Variables:
370 : : // mode: c++
371 : : // End:
|