Line data Source code
1 : /*
2 : * Copyright (c) 2020 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #ifndef BASE_FEATURE_FLAGS_H_
6 : #define BASE_FEATURE_FLAGS_H_
7 :
8 : #include <string>
9 : #include <mutex>
10 :
11 : #include <boost/function.hpp>
12 : #include <boost/scoped_ptr.hpp>
13 :
14 : #include <base/logging.h>
15 : #include <base/sandesh/process_info_constants.h>
16 : #include <base/sandesh/process_info_types.h>
17 :
18 : #include "rapidjson/document.h"
19 : #include "rapidjson/stringbuffer.h"
20 : #include "rapidjson/writer.h"
21 :
22 : /**
23 : * -----------------------------------------------------------------------------
24 : * C++ feature flag interface for developers
25 : * -----------------------------------------------------------------------------
26 : *
27 : * The Flag class serves as the interface for modules interested in one or
28 : * more feature flags.
29 : *
30 : * Developers bringing in new features can declare a flag in global scope.
31 : * Construction requires specifying a name, description of the flag, and a
32 : * default value.
33 : *
34 : * Flag flag_enable_hash_v2("Hash V2", "Enable the use of
35 : * the new generation hash table", false);
36 : *
37 : * A flag can be declared as belonging to the instance of an object. This
38 : * means that it can have a different definition per object.
39 : *
40 : * class Module {
41 : * public:
42 : * Module(const std::string interface_name, const Options& options);
43 : * private:
44 : * Flag enable_hash_v2_;
45 : * HashTable* ht_;
46 : * };
47 : *
48 : * When a module is instantiated, the internal flag is initialized. The
49 : * initialization uses the global flag definition and can be personalized.
50 : * The second parameter is a key-value dict that provides the context info
51 : * and the third parameter is a callback that will be invoked if there are
52 : * configuration or run-time updates to the feature flag definition.
53 : *
54 : * Module::Module(const std::string name, const Options& options)
55 : * : enable_hash_v2_(flag_enable_hash_v2,
56 : * {"interface", interface_name},
57 : * flag_update_cb) {
58 : * if (enable_hash_v2_.Get()) {
59 : * ht_ = NewFancyHashTable();
60 : * } else {
61 : * ht_ = OldHashTable();
62 : * }
63 : * }
64 : *
65 : * The FlagConfig class serves as an interface for storing user configuration
66 : * for features.
67 : * User configuration for feature flag is provided to the FlagManager
68 : * using this class. Used by servers providing config data (ifmap_server)
69 : * Data provided includes
70 : * 1. Flag name
71 : * 2. bool indicating if flag is enabled
72 : * 3. bool indicating default value
73 : * 4. Release in which the flag was introduced
74 : * 5. Context Info (optional)
75 : *
76 : * The FlagManager class is a flag store that maintains the list of feature
77 : * flags modules are interested in and the user configuration for them if any.
78 : * When the Flag class is instantiated, it automatically registers itself with
79 : * the flag store. When the FlagConfig class is instantiated, it informs the
80 : * flag store of the user configuration for flags. The FlagManager class on
81 : * receiving the user configuration for a flag will update the flag and sets
82 : * the "enabled" field in accordance with the user configuration. In addition,
83 : * it will invoke any callbacks registered by modules.
84 : *
85 : * A FlagUveManager class is responsible for providing functionality to interface
86 : * with analytics/introspect to provide data on the feature flags configured
87 : * in each module. Analytics registers callbacks using this class which will
88 : * be called when there is a change to flag definition or configuration.
89 : * -----------------------------------------------------------------------------
90 : */
91 :
92 : namespace process {
93 :
94 : class FlagManager;
95 :
96 :
97 : /**
98 : * --------------------------------------------------------------------------
99 : * Helper structs for representing flag config, state and context
100 : * --------------------------------------------------------------------------
101 : */
102 :
103 : struct FlagState {
104 : enum Type {
105 : EXPERIMENTAL = 0,
106 : ALPHA = 1,
107 : BETA = 2,
108 : IN_PROGRESS = 3,
109 : PRE_RETIRED = 4,
110 : UNKNOWN = 5
111 : };
112 :
113 10 : static std::string ToString(Type state) {
114 10 : switch (state) {
115 10 : case EXPERIMENTAL:
116 10 : return "Experimental";
117 : break;
118 0 : case ALPHA:
119 0 : return "Alpha";
120 : break;
121 0 : case BETA:
122 0 : return "Beta";
123 : break;
124 0 : case IN_PROGRESS:
125 0 : return "In Progress";
126 : break;
127 0 : case PRE_RETIRED:
128 0 : return "Pre Retired";
129 : break;
130 0 : case UNKNOWN:
131 0 : return "Unknown";
132 : break;
133 : }
134 0 : return "Unknown";
135 : }
136 :
137 : static Type FromString(const std::string& type) {
138 : if (type == "Experimental") {
139 : return EXPERIMENTAL;
140 : }
141 : if (type == "Alpha") {
142 : return ALPHA;
143 : }
144 : if (type == "Beta") {
145 : return BETA;
146 : }
147 : if (type == "In-Progress") {
148 : return IN_PROGRESS;
149 : }
150 : if (type == "Pre-Retired") {
151 : return PRE_RETIRED;
152 : }
153 : if (type == "Unknown") {
154 : return UNKNOWN;
155 : }
156 : return UNKNOWN;
157 : }
158 : };
159 :
160 : struct FlagContext {
161 4 : FlagContext(const std::string& description, const std::string &val)
162 4 : : desc(description),
163 4 : value(val) {}
164 :
165 22 : bool operator == (const FlagContext & rhs) const {
166 22 : if (!(desc == rhs.desc))
167 6 : return false;
168 16 : if (!(value == rhs.value))
169 6 : return false;
170 10 : return true;
171 : }
172 : bool operator != (const FlagContext &rhs) const {
173 : return !(*this == rhs);
174 : }
175 :
176 : std::string desc;
177 : std::string value;
178 : };
179 :
180 : typedef std::vector<FlagContext> ContextVec;
181 : typedef std::vector<FlagContext>::const_iterator context_iterator;
182 : typedef std::vector<FlagContext>::size_type context_size;
183 :
184 : class FlagConfig {
185 : public:
186 8 : FlagConfig(const std::string &name,const std::string &version,
187 : bool enabled, FlagState::Type state, ContextVec &context_infos)
188 8 : : name_(name),
189 8 : version_(version),
190 8 : enabled_(enabled),
191 8 : state_(state),
192 8 : context_infos_(context_infos) {}
193 126 : ~FlagConfig() {};
194 :
195 : /**
196 : * Getter/Setter functions for members
197 : */
198 : void set_name(const std::string &val) { name_ = val; }
199 87 : const std::string& name() const { return name_; }
200 :
201 : void set_version(const std::string &val) { version_ = val; }
202 10 : const std::string& version() const { return version_; }
203 :
204 : void set_enabled(bool val) { enabled_ = val; }
205 22 : bool enabled() const { return enabled_; }
206 :
207 : void set_state(const FlagState::Type &val) { state_ = val; }
208 10 : const FlagState::Type& state() const { return state_; }
209 :
210 : void set_context_infos(const ContextVec &val) { context_infos_ = val; }
211 22 : const ContextVec &context_infos() const { return context_infos_; }
212 :
213 2 : bool operator == (const FlagConfig &rhs) const {
214 2 : if (!(name_ == rhs.name_))
215 0 : return false;
216 2 : if (!(version_ == rhs.version_))
217 0 : return false;
218 2 : if (!(enabled_ == rhs.enabled_))
219 1 : return false;
220 1 : if (!(state_ == rhs.state_))
221 0 : return false;
222 1 : if (!(context_infos_ == rhs.context_infos_))
223 1 : return false;
224 0 : return true;
225 : }
226 :
227 2 : bool operator != (const FlagConfig &rhs) const {
228 2 : return !(*this == rhs);
229 : }
230 : private:
231 : std::string name_;
232 : std::string version_;
233 : bool enabled_;
234 : FlagState::Type state_;
235 : ContextVec context_infos_;
236 : };
237 :
238 : typedef std::vector<FlagConfig> FlagConfigVec;
239 : typedef std::vector<FlagConfig>::const_iterator flag_cfg_itr;
240 :
241 :
242 : /**
243 : * ----------------------------------------------------------------------------
244 : * Class representing a feature flag
245 : *
246 : * Modules can use this class to define flags they are interested in.
247 : * Data provided by module includes
248 : * 1. Flag name
249 : * 2. Description
250 : * 3. Context Info (optional)
251 : * 4. Callback for run-time updates (optional)
252 : * The Flag class will in turn register this Flag with the Flag Manager.
253 : *
254 : * ----------------------------------------------------------------------------
255 : */
256 :
257 : class Flag
258 : {
259 : public:
260 : /**
261 : * ----------------------------------------------------------------------
262 : * Callback provided by module to track run-time updates to feature
263 : * flag configuration.
264 : * ----------------------------------------------------------------------
265 : */
266 : typedef boost::function<void ()> FlagStateCb;
267 :
268 : /**
269 : * This constructor is used to create a feature flag with basic
270 : * information; name, description, default behavior and optional
271 : * context information.
272 : */
273 : Flag(FlagManager *manager, const std::string &name,
274 : const std::string &description, bool enabled,
275 : ContextVec &context_infos);
276 :
277 : /**
278 : * This constructor takes a Flag object with basic information with
279 : * provision for components to act on run-time updates through a callback
280 : */
281 : Flag(const Flag& flag, FlagStateCb callback);
282 :
283 : /**
284 : * Default Constructor
285 : */
286 : Flag() {};
287 : ~Flag();
288 :
289 : /**
290 : * Method to invoke callback provided by modules for this flag
291 : */
292 : void InvokeCb();
293 :
294 : /**
295 : * Getter/Setter functions for members
296 : */
297 : void set_name(const std::string &val) { name_ = val; }
298 15 : const std::string& name() const { return name_; }
299 :
300 : void set_description(const std::string &val) { description_ = val; }
301 : const std::string& description() const { return description_; }
302 :
303 4 : void set_enabled(const bool val) { enabled_ = val; }
304 9 : const bool enabled() const { return enabled_; }
305 :
306 : void set_context_infos(const ContextVec &val) { context_infos_ = val; }
307 4 : const ContextVec &context_infos() const { return context_infos_; }
308 :
309 : bool operator == (const Flag &rhs) const;
310 : bool operator != (const Flag &rhs) const;
311 : private:
312 : std::string name_;
313 : std::string description_;
314 : bool enabled_;
315 : ContextVec context_infos_;
316 :
317 : FlagStateCb flag_state_cb_;
318 : FlagManager *manager_;
319 :
320 : DISALLOW_COPY_AND_ASSIGN(Flag);
321 : };
322 :
323 : typedef std::vector<Flag> FlagVec;
324 :
325 : /**
326 : * -----------------------------------------------------------------------------
327 : * User configuration for feature flag is provided to the FlagManager
328 : * using this class. Used by servers providing config data (ifmap_server)
329 : * Data provided includes
330 : * 1. Flag name
331 : * 2. bool indicating if flag is enabled
332 : * 3. bool indicating default value
333 : * 4. Release in which the flag was introduced
334 : * 5. Context Info (optional)
335 : * -----------------------------------------------------------------------------
336 : */
337 : class FlagConfigManager {
338 : public:
339 : static FlagConfigManager* GetInstance();
340 : static void Initialize(const string &build_info);
341 :
342 : /**
343 : * API to set/update user config. Called when run-time updates are
344 : * received for feature.
345 : */
346 : void Set(const std::string &name,const std::string &version_info,
347 : bool enabled, FlagState::Type state, ContextVec &context_infos);
348 :
349 : /**
350 : * API to unset user config. Called when user unconfigures feature flag.
351 : */
352 : void Unset(const std::string &name);
353 :
354 : private:
355 : // Singleton
356 : FlagConfigManager(FlagManager *manager);
357 :
358 : static boost::scoped_ptr<FlagConfigManager> instance_;
359 : FlagManager *flag_manager_;
360 :
361 : static std::string version_;
362 :
363 : DISALLOW_COPY_AND_ASSIGN(FlagConfigManager);
364 : };
365 :
366 : // ----------------------------------------------------------------------------
367 :
368 : /**
369 : * -----------------------------------------------------------------------------
370 : *
371 : * FlagUveManager class responsible for providing functionality to interface
372 : * with analytics/introspect to provide data on the feature flags configured
373 : * in each module. Analytics registers callbacks using this class which will
374 : * be called when there is a change to flag definition or configuration.
375 : *
376 : * -----------------------------------------------------------------------------
377 : */
378 : class FlagUveManager {
379 : public:
380 : static FlagUveManager* GetInstance();
381 :
382 : /**
383 : * --------------------
384 : * Analytics Callbacks
385 : * --------------------
386 : */
387 :
388 : /**
389 : * Helper class handling analytics registers a callback(flag_uve_cb) with
390 : * FlagUveManager. This will be invoked when the FlagUveManager
391 : * processes any user configuration for the flags.
392 : */
393 : void SendUVE();
394 :
395 : /**
396 : * API to get all user-configured flags
397 : */
398 : FlagConfigVec GetFlagInfos(bool lock) const;
399 : private:
400 : friend class ConnectionStateManager;
401 :
402 : /**
403 : * UVE callback from ConnectionStateManager. This is called
404 : * to report information on feature flags capability by user
405 : * that modules are interested in.
406 : */
407 : typedef boost::function<void (void)> FlagUveCb;
408 :
409 : // Singleton
410 : FlagUveManager(FlagManager *manager, FlagUveCb send_uve_cb);
411 : static void CreateInstance(FlagManager *mgr, FlagUveCb send_uve_cb);
412 :
413 : static boost::scoped_ptr<FlagUveManager> instance_;
414 : FlagManager *flag_manager_;
415 : FlagUveCb flag_uve_cb_;
416 :
417 : DISALLOW_COPY_AND_ASSIGN(FlagUveManager);
418 : };
419 :
420 : // ----------------------------------------------------------------------------
421 :
422 : /**
423 : * -----------------------------------------------------------------------------
424 : *
425 : * FlagManager class responsible for providing functionality to maintain both
426 : * feature flags capability by users and the feature modules are interested in.
427 : * It will also interface with analytics/introspect to provide data on the
428 : * feature flags capability in each module.
429 : *
430 : * Accordingly, the class will provide APIs
431 : * 1. to interface with the north-bound server providing information on user
432 : * capability feature flags and the feature flags available in the system
433 : * (capability list)
434 : * 2. to interface with the modules. These include client APIs for the modules
435 : * a. to query if a feature is enabled/disabled
436 : * b. to get user capability information for a feature flag
437 : * c. to capture module interest in relevant feature flags
438 : * 3. to interface with analytics and send module level feature flag information
439 : * to analytics/introspect
440 : *
441 : * -----------------------------------------------------------------------------
442 : */
443 :
444 : class FlagManager
445 : {
446 : public:
447 : static FlagManager* GetInstance();
448 :
449 : /**
450 : * -------------
451 : * FlagMap APIs
452 : * -------------
453 : */
454 :
455 : /**
456 : * Process feature flags config and update FlagMap
457 : */
458 : void Set(const std::string &name, const std::string &version, bool enabled,
459 : FlagState::Type state, ContextVec &context_infos);
460 :
461 : /**
462 : * Feature flag removed from config. Process delete.
463 : */
464 : void Unset(const std::string &name);
465 :
466 : /**
467 : * Request from module to check if a feature flag is enabled
468 : * Module provides flag name and context for which it wants
469 : * to check if flag is enabled.
470 : */
471 : bool IsFlagEnabled(const std::string &name, bool default_state,
472 : const ContextVec &c_vec) const;
473 :
474 : /**
475 : * Remove all flag data from FlagMap
476 : */
477 : void ClearFlags();
478 :
479 : /**
480 : * Get the number of flags in FlagMap
481 : */
482 : size_t GetFlagMapCount() const;
483 :
484 : /**
485 : * -----------------
486 : * InterestMap APIs
487 : * -----------------
488 : */
489 :
490 : /**
491 : * Update InterestMap with feature flags modules are interested in.
492 : * The Flag object will be updated when user configuration is received by
493 : * the FlagManager.
494 : */
495 : void Register(Flag *flag);
496 :
497 : /**
498 : * Module is no longer interested in the flag. Remove from InterestMap
499 : */
500 : void Unregister(const Flag *flag);
501 :
502 : /**
503 : * Check if module has registered this feature flag
504 : */
505 : bool IsRegistered(const Flag *flag) const;
506 :
507 : /**
508 : * Get the number of flags in InterestMap
509 : */
510 : size_t GetIntMapCount() const;
511 :
512 : /**
513 : * ---------------------
514 : * Analytics helper APIs
515 : * ---------------------
516 : */
517 :
518 : /**
519 : * API for helper class to get flag configuration information.
520 : * Returns vector<FlagConfig>
521 : */
522 : FlagConfigVec GetFlagInfos() const;
523 :
524 : private:
525 : friend class Flag;
526 : friend class FlagConfigManager;
527 : friend class FlagUveManager;
528 :
529 48 : FlagManager() {
530 48 : }
531 :
532 : /**
533 : * =============================================
534 : * FlagMap - User Configuration/Capability list
535 : * =============================================
536 : */
537 :
538 : /**
539 : * Map that maintains flag information capability by user
540 : * User configures flag name, whether it is enabled and
541 : * optional flag context info.
542 : */
543 : typedef std::map<std::string, FlagConfig> FlagMap;
544 : typedef std::map<std::string, FlagConfig>::iterator flag_map_itr;
545 : typedef std::map<std::string, FlagConfig>::const_iterator flag_map_citr;
546 :
547 : /**
548 : * ===================================
549 : * InterestMap - Module interest list
550 : * ===================================
551 : */
552 :
553 : /**
554 : * Map that maintains flags modules are interested in
555 : * Modules provide name, description and a default value
556 : * NOTE: modules can define the same flag in multiple ways based on
557 : * varying the context. Hence the InteretMap can have multiple entries
558 : * for the same flag. For a given (flag name, context_info) though
559 : * there will be one unique entry in the InterestMap
560 : */
561 : typedef std::multimap<std::string, Flag *> InterestMap;
562 : typedef std::multimap<std::string, Flag *>::const_iterator int_map_const_itr;
563 : typedef std::multimap<std::string, Flag *>::iterator int_map_itr;
564 :
565 : // ==============================================================
566 :
567 : FlagConfigVec GetFlagInfosUnlocked() const;
568 :
569 : static boost::scoped_ptr<FlagManager> instance_;
570 : mutable std::mutex mutex_;
571 :
572 : FlagMap flag_map_; // map for capability-list/user-config
573 : InterestMap int_map_; // map for storing module interest
574 :
575 : DISALLOW_COPY_AND_ASSIGN(FlagManager);
576 : };
577 :
578 :
579 : } // namespace process
580 : #endif // BASE_FEATURE_FLAGS_H_
|