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