summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/cfg_option.h
blob: a0c6d0b68d3f3a8bbe1c1d840e974660f3165759 (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
317
318
319
320
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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
// Copyright (C) 2014-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/.

#ifndef CFG_OPTION_H
#define CFG_OPTION_H

#include <dhcp/option.h>
#include <dhcp/option_space_container.h>
#include <cc/cfg_to_element.h>
#include <cc/stamped_element.h>
#include <cc/user_context.h>
#include <dhcpsrv/cfg_option_def.h>
#include <dhcpsrv/key_from_key.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <list>
#include <string>
#include <vector>

namespace isc {
namespace dhcp {

class OptionDescriptor;

/// A pointer to option descriptor.
typedef boost::shared_ptr<OptionDescriptor> OptionDescriptorPtr;

/// A list of option descriptors.
typedef std::vector<OptionDescriptor> OptionDescriptorList;

/// @brief Option descriptor.
///
/// Option descriptor holds instance of an option and additional information
/// for this option. This information comprises whether this option is sent
/// to DHCP client only on request (persistent = false) or always
/// (persistent = true), or must never send (cancelled = true).
class OptionDescriptor : public data::StampedElement, public data::UserContext {
public:
    /// @brief Option instance.
    OptionPtr option_;

    /// @brief Persistence flag.
    ///
    /// If true, option is always sent to the client. If false, option is
    /// sent to the client when requested using ORO or PRL option.
    bool persistent_;

    /// @brief Cancelled flag.
    ///
    /// If true, option is never sent to the client. If false, option is
    /// sent when it should.
    /// @note: When true the action of this flag is final i.e. it can't be
    /// overridden at a more specific level and has precedence over persist.
    bool cancelled_;

    /// @brief Option value in textual (CSV) format.
    ///
    /// This field is used to convey option value in human readable format,
    /// the same as used to specify option value in the server configuration.
    /// This value is optional and can be held in the host reservations
    /// database instead of the binary format.
    ///
    /// Note that this value is carried in the option descriptor, rather than
    /// @c Option instance because it is a server specific value (same as
    /// persistence flag).
    ///
    /// An example of the formatted value is: "2001:db8:1::1, 23, some text"
    /// for the option which carries IPv6 address, a number and a text.
    std::string formatted_value_;

    /// @brief Option space name.
    ///
    /// Options are associated with option spaces. Typically, such association
    /// is made when the option is stored in the @c OptionContainer. However,
    /// in some cases it is also required to associate option with the particular
    /// option space outside of the container. In particular, when the option
    /// is fetched from a database. The database configuration backend will
    /// set option space upon return of the option. In other cases this value
    /// won't be set.
    std::string space_name_;

    /// @brief Constructor.
    ///
    /// @param opt option instance.
    /// @param persist if true, option is always sent.
    /// @param cancel if true, option is never sent.
    /// @param formatted_value option value in the textual format (optional).
    /// @param user_context user context (optional).
    OptionDescriptor(const OptionPtr& opt, bool persist, bool cancel,
                     const std::string& formatted_value = "",
                     data::ConstElementPtr user_context = data::ConstElementPtr())
        : data::StampedElement(), option_(opt), persistent_(persist),
          cancelled_(cancel), formatted_value_(formatted_value),
          space_name_() {
        setContext(user_context);
    };

    /// @brief Constructor.
    ///
    /// @param persist if true option is always sent.
    /// @param cancel if true, option is never sent.
    OptionDescriptor(bool persist, bool cancel)
        : data::StampedElement(), option_(OptionPtr()), persistent_(persist),
          cancelled_(cancel), formatted_value_(), space_name_() {};

    /// @brief Copy constructor.
    ///
    /// @param desc option descriptor to be copied.
    OptionDescriptor(const OptionDescriptor& desc)
        : data::StampedElement(desc),
          option_(desc.option_),
          persistent_(desc.persistent_),
          cancelled_(desc.cancelled_),
          formatted_value_(desc.formatted_value_),
          space_name_(desc.space_name_) {
        setContext(desc.getContext());
    };

    /// @brief Assignment operator.
    ///
    /// @param other option descriptor to be assigned from.
    OptionDescriptor& operator=(const OptionDescriptor& other) {
        if (this != &other) {
            // Not self-assignment.
            data::StampedElement::operator=(other);
            option_ = other.option_;
            persistent_ = other.persistent_;
            cancelled_ = other.cancelled_;
            formatted_value_ = other.formatted_value_;
            space_name_ = other.space_name_;
            setContext(other.getContext());
        }
        return (*this);
    }

    /// @brief Factory function creating an instance of the @c OptionDescriptor.
    ///
    /// @param opt option instance.
    /// @param persist if true, option is always sent.
    /// @param cancel if true, option is never sent.
    /// @param formatted_value option value in the textual format (optional).
    /// @param user_context user context (optional).
    ///
    /// @return Pointer to the @c OptionDescriptor instance.
    static OptionDescriptorPtr create(const OptionPtr& opt,
                                      bool persist,
                                      bool cancel,
                                      const std::string& formatted_value = "",
                                      data::ConstElementPtr user_context =
                                      data::ConstElementPtr());

    /// @brief Factory function creating an instance of the @c OptionDescriptor.
    ///
    /// @param persist if true option is always sent.
    /// @param cancel if true, option is never sent.
    ///
    /// @return Pointer to the @c OptionDescriptor instance.
    static OptionDescriptorPtr create(bool persist, bool cancel);

    /// @brief Factory function creating an instance of the @c OptionDescriptor.
    ///
    /// @param desc option descriptor to be copied.
    ///
    /// @return Pointer to the @c OptionDescriptor instance.
    static OptionDescriptorPtr create(const OptionDescriptor& desc);

    /// @brief Checks if the one descriptor is equal to another.
    ///
    /// @param other Other option descriptor to compare to.
    ///
    /// @return true if descriptors equal, false otherwise.
    bool equals(const OptionDescriptor& other) const;

    /// @brief Equality operator.
    ///
    /// @param other Other option descriptor to compare to.
    ///
    /// @return true if descriptors equal, false otherwise.
    bool operator==(const OptionDescriptor& other) const {
        return (equals(other));
    }

    /// @brief Inequality operator.
    ///
    /// @param other Other option descriptor to compare to.
    ///
    /// @return true if descriptors unequal, false otherwise.
    bool operator!=(const OptionDescriptor& other) const {
        return (!equals(other));
    }
};

/// @brief Multi index container for DHCP option descriptors.
///
/// This container comprises three indexes to access option
/// descriptors:
/// - sequenced index: used to access elements in the order they
/// have been added to the container,
/// - option type index: used to search option descriptors containing
/// options with specific option code (aka option type).
/// - persistency flag index: used to search option descriptors with
/// 'persistent' flag set to true.
///
/// This container is the equivalent of four separate STL containers:
/// - std::list of all options,
/// - std::multimap of options with option code used as a multimap key,
/// - std::multimap of option descriptors with option persistency flag
/// used as a multimap key.
/// - std::multimap of option descriptors with option cancellation flag
/// used as a multimap key.
/// The major advantage of this container over 4 separate STL containers
/// is automatic synchronization of all indexes when elements are added,
/// removed or modified in the container. With separate containers,
/// the synchronization would have to be guaranteed by the Subnet class
/// code. This would increase code complexity and presumably it would
/// be much harder to add new search criteria (indexes).
///
/// @todo we may want to search for options using option spaces when
/// they are implemented.
///
/// @see http://www.boost.org/doc/libs/1_51_0/libs/multi_index/doc/index.html
typedef boost::multi_index_container<
    // Container comprises elements of OptionDescriptor type.
    OptionDescriptor,
    // Here we start enumerating various indexes.
    boost::multi_index::indexed_by<
        // Sequenced index allows accessing elements in the same way
        // as elements in std::list.
        // Sequenced is an index #0.
        boost::multi_index::sequenced<>,
        // Start definition of index #1.
        boost::multi_index::hashed_non_unique<
            // KeyFromKeyExtractor is the index key extractor that allows
            // accessing option type being held by the OptionPtr through
            // OptionDescriptor structure.
            KeyFromKeyExtractor<
                // Use option type as the index key. The type is held
                // in OptionPtr object so we have to call Option::getType
                // to retrieve this key for each element.
                boost::multi_index::const_mem_fun<
                    Option,
                    uint16_t,
                    &Option::getType
                >,
                // Indicate that OptionPtr is a member of
                // OptionDescriptor structure.
                boost::multi_index::member<
                    OptionDescriptor,
                    OptionPtr,
                    &OptionDescriptor::option_
                 >
            >
        >,
        // Start definition of index #2.
        // Use 'persistent' struct member as a key.
        boost::multi_index::hashed_non_unique<
            boost::multi_index::member<
                OptionDescriptor,
                bool,
                &OptionDescriptor::persistent_
            >
        >,
        // Start definition of index #3.
        // Use BaseStampedElement::getModificationTime as a key.
        boost::multi_index::ordered_non_unique<
            boost::multi_index::const_mem_fun<
                data::BaseStampedElement,
                boost::posix_time::ptime,
                &data::BaseStampedElement::getModificationTime
            >
        >,

        // Start definition of index #4.
        // Use BaseStampedElement::getId as a key.
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<OptionIdIndexTag>,
            boost::multi_index::const_mem_fun<data::BaseStampedElement, uint64_t,
                                              &data::BaseStampedElement::getId>
        >,
        // Start definition of index #5.
        // Use 'cancelled' struct member as a key.
        boost::multi_index::hashed_non_unique<
            boost::multi_index::member<
                OptionDescriptor,
                bool,
                &OptionDescriptor::cancelled_
            >
        >
    >
> OptionContainer;

/// Pointer to the OptionContainer object.
typedef boost::shared_ptr<OptionContainer> OptionContainerPtr;
/// Type of the index #1 - option type.
typedef OptionContainer::nth_index<1>::type OptionContainerTypeIndex;
/// Pair of iterators to represent the range of options having the
/// same option type value. The first element in this pair represents
/// the beginning of the range, the second element represents the end.
typedef std::pair<OptionContainerTypeIndex::const_iterator,
                  OptionContainerTypeIndex::const_iterator> OptionContainerTypeRange;
/// Type of the index #2 - option persistency flag.
typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex;
/// Pair of iterators to represent the range of options having the
/// same persistency flag. The first element in this pair represents
/// the beginning of the range, the second element represents the end.
typedef std::pair<OptionContainerPersistIndex::const_iterator,
                  OptionContainerPersistIndex::const_iterator> OptionContainerPersistRange;
/// Type of the index #5 - option cancellation flag.
typedef OptionContainer::nth_index<5>::type OptionContainerCancelIndex;
/// Pair of iterators to represent the range of options having the
/// same cancellation flag. The first element in this pair represents
/// the beginning of the range, the second element represents the end.
typedef std::pair<OptionContainerCancelIndex::const_iterator,
                  OptionContainerCancelIndex::const_iterator> OptionContainerCancelRange;

/// @brief Represents option data configuration for the DHCP server.
///
/// This class holds a collection of options to be sent to a DHCP client.
/// Options are grouped by the option space or vendor identifier (for
/// vendor options).
///
/// The server configuration allows for specifying two distinct collections
/// of options: global options and per-subnet options in which some options
/// may overlap.
///
/// The collection of global options specify options being sent to the client
/// belonging to any subnets, i.e. global options are "inherited" by all
/// subnets.
///
/// The per-subnet options are configured for a particular subnet and are sent
/// to clients which belong to this subnet. The values of the options specified
/// for a particular subnet override the values of the global options.
///
/// This class represents a single collection of options (either global or
/// per-subnet). Each subnet holds its own object of the @c CfgOption type. The
/// @c CfgMgr holds a @c CfgOption object representing global options.
///
/// Note that having a separate copy of the @c CfgOption to represent global
/// options is useful when the client requests stateless configuration from
/// the DHCP server and no subnet is selected for this client. This client
/// will only receive global options.
class CfgOption : public isc::data::CfgToElement {
public:

    /// @brief default constructor
    CfgOption();

    /// @brief Indicates the object is empty
    ///
    /// @return true when the object is empty
    bool empty() const;

    /// @name Methods and operators used for comparing objects.
    ///
    //@{
    /// @brief Check if configuration is equal to other configuration.
    ///
    /// @param other An object holding configuration to compare to.
    ///
    /// @return true if configurations are equal, false otherwise.
    bool equals(const CfgOption& other) const;

    /// @brief Equality operator.
    ///
    /// @param other An object holding configuration to compare to.
    ///
    /// @return true if configurations are equal, false otherwise.
    bool operator==(const CfgOption& other) const {
        return (equals(other));
    }

    /// @brief Inequality operator.
    ///
    /// @param other An object holding configuration to compare to.
    ///
    /// @return true if configurations are unequal, false otherwise.
    bool operator!=(const CfgOption& other) const {
        return (!equals(other));
    }

    //@}

    /// @brief Adds instance of the option to the configuration.
    ///
    /// There are two types of options which may be passed to this method:
    /// - vendor options
    /// - non-vendor options
    ///
    /// The non-vendor options are grouped by the name of the option space
    /// (specified in textual format). The vendor options are grouped by the
    /// vendor identifier, which is a 32-bit unsigned integer value.
    ///
    /// In order to add new vendor option to the list the option space name
    /// (last argument of this method) should be specified as "vendor-X" where
    /// "X" is a 32-bit unsigned integer, e.g. "vendor-1234". Options for which
    /// the @c option_space argument doesn't follow this format are added as
    /// non-vendor options.
    ///
    /// @param option Pointer to the option being added.
    /// @param persistent Boolean value which specifies if the option should
    /// be sent to the client regardless if requested (true), or nor (false)
    /// @param cancelled Boolean value which specifies if the option must
    /// never be sent to the client.
    /// @param option_space Option space name.
    /// @param id Optional database id to be associated with the option.
    ///
    /// @throw isc::BadValue if the option space is invalid.
    void add(const OptionPtr& option, const bool persistent,
             const bool cancelled, const std::string& option_space,
             const uint64_t id = 0);

    /// @brief A variant of the @ref CfgOption::add method which takes option
    /// descriptor as an argument.
    ///
    /// @param desc Option descriptor holding option instance and other
    /// parameters pertaining to the option.
    /// @param option_space Option space name.
    ///
    /// @throw isc::BadValue if the option space is invalid.
    void add(const OptionDescriptor& desc, const std::string& option_space);

    /// @brief Replaces the instance of an option within this collection
    ///
    /// This method locates the option within the given space and replaces
    /// it with a copy of the given descriptor.  This effectively updates
    /// the contents without altering the container indexing.
    ///
    /// @param desc Option descriptor holding option instance and other
    /// parameters pertaining to the option.
    /// @param option_space Option space name.
    ///
    /// @throw isc::BadValue if the descriptor's option instance is null,
    /// if  space is invalid, or if the option does not already exist
    /// in the given space.
    void replace(const OptionDescriptor& desc, const std::string& option_space);

    /// @brief Merges another option configuration into this one.
    ///
    /// This method calls @c mergeTo() to add this configuration's
    /// options into @c other (skipping any duplicates).  Next it calls
    /// @c createDescriptorOption() for each option descriptor in the
    /// merged set.  This (re)-creates each descriptor's option based on
    /// the merged set of opt definitions. Finally, it calls
    /// @c copyTo() to overwrite this configuration's options with
    /// the merged set in @c other.
    ///
    /// @warning The merge operation will affect the @c other configuration.
    /// Therefore, the caller must not rely on the data held in the @c other
    /// object after the call to @c merge. Also, the data held in @c other must
    /// not be modified after the call to @c merge because it may affect the
    /// merged configuration.
    ///
    /// @param cfg_def set of of user-defined option definitions to use
    /// when merging.
    /// @param other option configuration to merge in.
    void merge(CfgOptionDefPtr cfg_def, CfgOption& other);

    /// @brief Re-create the option in each descriptor based on given definitions
    ///
    /// Invokes @c createDescriptorOption() on each option descriptor in
    /// each option space, passing in the given dictionary of option
    /// definitions.  If the descriptor's option is re-created, then the
    /// descriptor is updated by calling @c replace().
    ///
    /// @param cfg_def set of of user-defined option definitions to use
    /// when creating option instances.
    void createOptions(CfgOptionDefPtr cfg_def);

    /// @brief Creates an option descriptor's option based on a set of option defs
    ///
    /// This function's primary use is to create definition specific options for
    /// option descriptors fetched from a configuration backend, as part of a
    /// configuration merge.
    ///
    /// Given an OptionDescriptor whose option_ member contains a generic option
    /// (i.e has a code and/or data), this function will attempt to find a matching
    /// definition and then use that definition's factory to create an option
    /// instance specific to that definition.   It will then replace the descriptor's
    /// generic option with the specific option.
    ///
    /// Three sources of definitions are searched, in the following order:
    ///
    /// 1. Standard option definitions (@c LIBDHCP::getOptionDef))
    /// 2. Vendor option definitions (@c LIBDHCP::getVendorOptionDef))
    /// 3. User specified definitions passed in via cfg_def parameter.
    ///
    /// The code will use the first matching definition found.  It then applies
    /// the following rules:
    ///
    /// -# If no definition is found but the descriptor conveys a non-empty
    /// formatted value, throw an error.
    /// -# If not definition is found and there is no formatted value, return
    /// This leaves intact the generic option in the descriptor.
    /// -# If a definition is found and there is no formatted value, pass the
    /// descriptor's generic option's data into the definition's factory. Replace
    /// the descriptor's option with the newly created option.
    /// -# If a definition is found and there is a formatted value, split
    /// the value into vector of values and pass that into the definition's
    /// factory. Replace the descriptor's option with the newly created option.
    ///
    /// @param cfg_def the user specified definitions to use
    /// @param space the option space name of the option
    /// @param opt_desc OptionDescriptor describing the option.
    ///
    /// @return True if the descriptor's option instance was replaced.
    /// @throw InvalidOperation if the descriptor conveys a formatted value and
    /// there is no definition matching the option code in the given space, or
    /// if the definition factory invocation fails.
    static bool createDescriptorOption(CfgOptionDefPtr cfg_def, const std::string& space,
                             OptionDescriptor& opt_desc);

    /// @brief Merges this configuration to another configuration.
    ///
    /// This method iterates over the configuration items held in this
    /// configuration and copies them to the configuration specified
    /// as a parameter. If an item exists in the destination it is not
    /// copied.
    ///
    /// @param [out] other Configuration object to merge to.
    void mergeTo(CfgOption& other) const;

    /// @brief Copies this configuration to another configuration.
    ///
    /// This method copies options configuration to another object.
    ///
    /// @param [out] other An object to copy the configuration to.
    void copyTo(CfgOption& other) const;

    /// @brief Appends encapsulated options to top-level options.
    ///
    /// This method iterates over the top-level options (from "dhcp4"
    /// and "dhcp6" option space) and checks which option spaces these
    /// options encapsulate. For each encapsulated option space, the
    /// options from this option space are appended to top-level options.
    void encapsulate();

    /// @brief Checks if options have been encapsulated.
    ///
    /// @return true if options have been encapsulated, false otherwise.
    bool isEncapsulated() const {
        return (encapsulated_);
    }

    /// @brief Returns all options for the specified option space.
    ///
    /// This method will not return vendor options, i.e. having option space
    /// name in the format of "vendor-X" where X is 32-bit unsigned integer.
    /// See @c getAll(uint32_t) for vendor options.
    ///
    /// @param option_space Name of the option space.
    ///
    /// @return Pointer to the container holding returned options. This
    /// container is empty if no options have been found.
    OptionContainerPtr getAll(const std::string& option_space) const;

    /// @brief Returns vendor options for the specified vendor id.
    ///
    /// @param vendor_id Vendor id for which options are to be returned.
    ///
    /// @return Pointer to the container holding returned options. This
    /// container is empty if no options have been found.
    OptionContainerPtr getAll(const uint32_t vendor_id) const;

    /// @brief Returns all non-vendor or vendor options for the specified
    /// option space.
    ///
    /// It combines the output of the @c getAll function variants. When
    /// option space has the format of "vendor-X", it retrieves the vendor
    /// options by vendor id, where X must be a 32-bit unsigned integer.
    /// Otherwise, it fetches non-vendor options.
    ///
    /// @param option_space Name of the option space.
    /// @return Pointer to the container holding returned options. This
    /// container is empty if no options have been found.
    OptionContainerPtr getAllCombined(const std::string& option_space) const;

    /// @brief Returns option for the specified key and option code.
    ///
    /// The key should be a string, in which case it specifies an option space
    /// name, or an uint32_t value, in which case it specifies a vendor
    /// identifier.
    ///
    /// @note If there are multiple options with the same key, only one will
    /// be returned.  No indication will be given of the presence of others,
    /// and the instance returned is not determinable. So please use
    /// the next method when multiple instances of the option are expected.
    ///
    /// @param key Option space name or vendor identifier.
    /// @param option_code Code of the option to be returned.
    /// @tparam Selector one of: @c std::string or @c uint32_t
    ///
    /// @return Descriptor of the option. If option hasn't been found, the
    /// descriptor holds null option.
    template<typename Selector>
    OptionDescriptor get(const Selector& key,
                         const uint16_t option_code) const {

        // Check for presence of options.
        OptionContainerPtr options = getAll(key);
        if (!options || options->empty()) {
            return (OptionDescriptor(false, false));
        }

        // Some options present, locate the one we are interested in.
        const OptionContainerTypeIndex& idx = options->get<1>();
        OptionContainerTypeIndex::const_iterator od_itr = idx.find(option_code);
        if (od_itr == idx.end()) {
            return (OptionDescriptor(false, false));
        }

        return (*od_itr);
    }

    /// @brief Returns options for the specified key and option code.
    ///
    /// The key should be a string, in which case it specifies an option space
    /// name, or an uint32_t value, in which case it specifies a vendor
    /// identifier.
    ///
    /// @param key Option space name or vendor identifier.
    /// @param option_code Code of the option to be returned.
    /// @tparam Selector one of: @c std::string or @c uint32_t
    ///
    /// @return List of Descriptors of the option.
    template<typename Selector>
    OptionDescriptorList getList(const Selector& key,
                                 const uint16_t option_code) const {

        OptionDescriptorList list;
        // Check for presence of options.
        OptionContainerPtr options = getAll(key);
        if (!options || options->empty()) {
            return (list);
        }

        // Some options present, locate the one we are interested in.
        const OptionContainerTypeIndex& idx = options->get<1>();
        OptionContainerTypeRange range = idx.equal_range(option_code);
        // This code copies descriptors and can be optimized not doing this.
        for (OptionContainerTypeIndex::const_iterator od_itr = range.first;
             od_itr != range.second; ++od_itr) {
            list.push_back(*od_itr);
        }

        return (list);
    }

    /// @brief Deletes option for the specified option space and option code.
    ///
    /// If the option is encapsulated within some non top level option space,
    /// it is also deleted from all option instances encapsulating this
    /// option space.
    ///
    /// @param option_space Option space name.
    /// @param option_code Code of the option to be returned.
    ///
    /// @return Number of deleted options.
    size_t del(const std::string& option_space, const uint16_t option_code);

    /// @brief Deletes vendor option for the specified vendor id.
    ///
    /// @param vendor_id Vendor identifier.
    /// @param option_code Option code.
    ///
    /// @return Number of deleted options.
    size_t del(const uint32_t vendor_id, const uint16_t option_code);

    /// @brief Deletes all options having a given database id.
    ///
    /// Note that there are cases when there will be multiple options
    /// having the same id (typically id of 0). When configuration backend
    /// is in use it sets the unique ids from the database. In cases when
    /// the configuration backend is not used, the ids default to 0.
    /// Passing the id of 0 would result in deleting all options that were
    /// not added via the database.
    ///
    /// Both regular and vendor specific options are deleted with this
    /// method.
    ///
    /// This method internally calls @c encapsulate() after deleting
    /// options having the given id.
    ///
    /// @param id Identifier of the options to be deleted.
    ///
    /// @return Number of deleted options. Note that if a single option
    /// instance is encapsulated by multiple options it adds 1 to the
    /// number of deleted options even though the same instance is
    /// deleted from multiple higher level options.
    size_t del(const uint64_t id);

    /// @brief Returns a list of configured option space names.
    ///
    /// The returned option space names exclude vendor option spaces,
    /// such as "vendor-1234". These are returned by the
    /// @ref getVendorIdsSpaceNames.
    ///
    /// @return List comprising option space names.
    std::list<std::string> getOptionSpaceNames() const {
        return (options_.getOptionSpaceNames());
    }

    /// @brief Returns a list of all configured  vendor identifiers.
    std::list<uint32_t> getVendorIds() const {
        return (vendor_options_.getOptionSpaceNames());
    }

    /// @brief Returns a list of option space names for configured vendor ids.
    ///
    /// For each vendor-id the option space name returned is constructed
    /// as "vendor-XYZ" where XYZ is a @c uint32_t value without leading
    /// zeros.
    ///
    /// @return List comprising option space names for vendor options.
    std::list<std::string> getVendorIdsSpaceNames() const;

    /// @brief Unparse a configuration object
    ///
    /// @return a pointer to unparsed configuration
    virtual isc::data::ElementPtr toElement() const;

    /// @brief Unparse a configuration object with optionally including
    /// the metadata.
    ///
    /// @param include_metadata boolean value indicating if the metadata
    /// should be included (if true) or not (if false).
    ///
    /// @return A pointer to the unparsed configuration.
    isc::data::ElementPtr
    toElementWithMetadata(const bool include_metadata) const;

private:

    /// @brief Appends encapsulated options to the options in an option space.
    ///
    /// This method appends sub-options to the options belonging to the
    /// particular option space. For example: if the option space "foo"
    /// is specified, this function will go over all options belonging to
    /// "foo" and will check which option spaces they encapsulate. For each
    /// such option it will retrieve options for these option spaces and append
    /// as sub-options to options belonging to "foo".
    ///
    /// @param option_space Name of the option space containing option to
    /// which encapsulated options are appended.
    void encapsulateInternal(const std::string& option_space);

    /// @brief Appends encapsulated options from the option space encapsulated
    /// by the specified option.
    ///
    /// This method will go over all options belonging to the encapsulated space
    /// and will check which option spaces they encapsulate recursively,
    /// adding these options to the current option
    ///
    /// @param option which encapsulated options.
    void encapsulateInternal(const OptionPtr& option);

    /// @brief Merges data from two option containers.
    ///
    /// This method merges options from one option container to another
    /// option container. This function is templated because containers
    /// may use different type of selectors. For non-vendor options
    /// the selector is of the @c std::string type, for vendor options
    /// the selector is of the @c uint32_t type.
    ///
    /// @param src_container Reference to a container from which the data
    /// will be merged.
    /// @param [out] dest_container Reference to a container to which the
    /// data will be merged.
    /// @tparam Type of the selector: @c std::string or @c uint32_t.
    template <typename Selector>
    void mergeInternal(const OptionSpaceContainer<OptionContainer,
                       OptionDescriptor, Selector>& src_container,
                       OptionSpaceContainer<OptionContainer,
                       OptionDescriptor, Selector>& dest_container) const;

    /// @brief A flag indicating if options have been encapsulated.
    bool encapsulated_;

    /// @brief Type of the container holding options grouped by option space.
    typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
                                 std::string> OptionSpaceCollection;
    /// @brief Container holding options grouped by option space.
    OptionSpaceCollection options_;

    /// @brief Type of the container holding options grouped by vendor id.
    typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
                                 uint32_t> VendorOptionSpaceCollection;
    /// @brief Container holding options grouped by vendor id.
    VendorOptionSpaceCollection vendor_options_;
};

/// @name Pointers to the @c CfgOption objects.
//@{
/// @brief Non-const pointer.
typedef boost::shared_ptr<CfgOption> CfgOptionPtr;

/// @brief Const pointer.
typedef boost::shared_ptr<const CfgOption> ConstCfgOptionPtr;

/// @brief Const pointer list.
typedef std::list<ConstCfgOptionPtr> CfgOptionList;

//@}

}
}

#endif // CFG_OPTION_H