Line data Source code
1 : /*
2 : * Copyright (c) 2019 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <string>
6 : #include <base/feature_flags.h>
7 : #include <assert.h>
8 : #include <map>
9 : #include <iostream>
10 : #include <sstream>
11 :
12 : using namespace std;
13 : using contrail_rapidjson::Document;
14 : using contrail_rapidjson::Value;
15 :
16 : namespace process {
17 :
18 : bool debug_ = true;
19 :
20 : #define FLAG_DEBUG(args...) if (process::debug_) LOG(DEBUG, ##args)
21 : #define FLAG_WARN(args...) if (process::debug_) LOG(WARN, ##args)
22 : #define FLAG_ERROR(args...) if (process::debug_) LOG(ERROR, ##args)
23 :
24 : /**
25 : * ------------------------------
26 : * Flag Class Method Implementations
27 : * ------------------------------
28 : */
29 :
30 : /**
31 : * Constructors/Destructor
32 : */
33 5 : Flag::Flag(FlagManager *manager, const string &name,
34 : const string &description, bool enabled,
35 5 : ContextVec &context_infos)
36 5 : : name_(name),
37 5 : description_(description),
38 5 : enabled_(enabled),
39 5 : context_infos_(context_infos),
40 5 : flag_state_cb_(NULL),
41 5 : manager_(manager)
42 : {
43 : /**
44 : * Add flag to FlagManager's capability list
45 : */
46 5 : manager_->Register(this);
47 5 : }
48 :
49 0 : Flag::Flag(const Flag& flag, FlagStateCb callback)
50 0 : : name_(flag.name_),
51 0 : description_(flag.description_),
52 0 : enabled_(flag.enabled_),
53 0 : context_infos_(flag.context_infos_),
54 0 : flag_state_cb_(callback),
55 0 : manager_(flag.manager_)
56 : {
57 : /**
58 : * Add flag to FlagManager's interest list
59 : */
60 0 : manager_->Register(this);
61 0 : }
62 :
63 5 : Flag::~Flag() {
64 5 : if (manager_) {
65 5 : manager_->Unregister(this);
66 : }
67 5 : }
68 :
69 : /**
70 : * Member Functions
71 : */
72 :
73 4 : void Flag::InvokeCb() {
74 4 : if (!flag_state_cb_.empty()) {
75 0 : flag_state_cb_();
76 : }
77 4 : }
78 :
79 0 : bool Flag::operator == (const Flag &rhs) const {
80 0 : if (!(name_ == rhs.name_))
81 0 : return false;
82 0 : if (!(description_ == rhs.description_))
83 0 : return false;
84 0 : if (!(enabled_ == rhs.enabled_))
85 0 : return false;
86 0 : if (!(context_infos_ == rhs.context_infos_))
87 0 : return false;
88 0 : return true;
89 : }
90 :
91 0 : bool Flag::operator != (const Flag &rhs) const {
92 0 : return !(*this == rhs);
93 : }
94 :
95 : /**
96 : * ---------------------------------------------
97 : * FlagUveManager Class Method Implementations
98 : * ----------------------------------------------
99 : */
100 :
101 : boost::scoped_ptr<FlagUveManager> FlagUveManager::instance_;
102 :
103 48 : FlagUveManager::FlagUveManager(FlagManager *manager,
104 48 : FlagUveCb flag_uve_cb)
105 48 : : flag_manager_(manager),
106 48 : flag_uve_cb_(flag_uve_cb) {
107 48 : }
108 :
109 : /**
110 : * CreateInstance should be called from ConnectionStateManager::Init()
111 : */
112 48 : void FlagUveManager::CreateInstance(FlagManager *manager,
113 : FlagUveCb flag_uve_cb) {
114 : /**
115 : * The assert is to catch errors where FlagManager::GetInstance()
116 : * is called before ConnectionStateManager::Init()
117 : */
118 48 : assert(instance_ == NULL);
119 48 : instance_.reset(new FlagUveManager(manager, flag_uve_cb));
120 48 : }
121 :
122 467 : FlagUveManager* FlagUveManager::GetInstance() {
123 467 : if (instance_ == NULL) {
124 0 : instance_.reset(new FlagUveManager(NULL, NULL));
125 : }
126 467 : return instance_.get();
127 : }
128 :
129 14 : void FlagUveManager::SendUVE() {
130 14 : if (!flag_uve_cb_.empty()) {
131 14 : flag_uve_cb_();
132 : } else {
133 0 : FLAG_WARN("SendUVE cb not set");
134 : }
135 14 : }
136 :
137 453 : FlagConfigVec FlagUveManager::GetFlagInfos(bool lock) const {
138 453 : if (lock) {
139 0 : return (flag_manager_->GetFlagInfos());
140 : } else {
141 453 : return (flag_manager_->GetFlagInfosUnlocked());
142 : }
143 : }
144 :
145 : /**
146 : * -------------------------------------
147 : * FlagManager Class Method Implementations
148 : * --------------------------------------
149 : */
150 :
151 : /**
152 : * FlagConfigManager instance
153 : */
154 : boost::scoped_ptr<FlagConfigManager> FlagConfigManager::instance_;
155 : string FlagConfigManager::version_;
156 :
157 1 : FlagConfigManager::FlagConfigManager(FlagManager* manager)
158 1 : : flag_manager_(manager) {
159 1 : }
160 :
161 : /**
162 : * Initialize should be called from modules (for instance from main)
163 : */
164 1 : void FlagConfigManager::Initialize(const string &build_info) {
165 1 : if (instance_ == NULL) {
166 0 : instance_.reset(new FlagConfigManager(FlagManager::GetInstance()));
167 : }
168 :
169 1 : Document d;
170 1 : if ( d.Parse<0>(build_info.c_str()).HasParseError() ) {
171 0 : FLAG_WARN("Parsing error");
172 : } else {
173 1 : const Value &v = d["build-info"];
174 1 : version_ = v[0]["build-version"].GetString();
175 : }
176 1 : }
177 :
178 1 : FlagConfigManager* FlagConfigManager::GetInstance() {
179 1 : if (instance_ == NULL) {
180 1 : instance_.reset(new FlagConfigManager(FlagManager::GetInstance()));
181 : }
182 1 : return instance_.get();
183 : }
184 :
185 9 : void FlagConfigManager::Set(const string &name,
186 : const string &version, bool enabled,
187 : FlagState::Type state,
188 : ContextVec &context_infos) {
189 : /**
190 : * Call FlagManager to add/update the configuration for this feature flag
191 : * to its flag store if the version matches the module version.
192 : * Otherwise ignore.
193 : */
194 9 : if (version_ != version) {
195 1 : FLAG_DEBUG("Flag " << name << "'s version " << version <<
196 : " does not match with module version " << version_ <<
197 : " Ignoring.");
198 1 : return;
199 : }
200 8 : flag_manager_->Set(name, version, enabled, state, context_infos);
201 8 : FlagUveManager::GetInstance()->SendUVE();
202 : }
203 :
204 6 : void FlagConfigManager::Unset(const string &name) {
205 : /**
206 : * User unconfigured the flag. Call FlagManager to remove the
207 : * user configuration for this feature flag from its flag store.
208 : */
209 6 : flag_manager_->Unset(name);
210 6 : FlagUveManager::GetInstance()->SendUVE();
211 6 : }
212 :
213 : /**
214 : * -------------------------------------
215 : * FlagManager Class Method Implementations
216 : * --------------------------------------
217 : */
218 :
219 : /**
220 : * FlagManager instance
221 : */
222 : boost::scoped_ptr<FlagManager> FlagManager::instance_;
223 :
224 50 : FlagManager* FlagManager::GetInstance() {
225 50 : if (instance_ == NULL) {
226 48 : instance_.reset(new FlagManager());
227 : }
228 50 : return instance_.get();
229 : }
230 :
231 0 : void FlagManager::ClearFlags() {
232 0 : flag_map_.clear();
233 0 : }
234 :
235 15 : size_t FlagManager::GetFlagMapCount() const {
236 15 : return (flag_map_.size());
237 : }
238 :
239 8 : void FlagManager::Set(const string &name, const string &version,
240 : bool enabled, FlagState::Type state,
241 : ContextVec &context_infos) {
242 : /**
243 : * Create a FlagConfig instance and add it to FlagMap if not present.
244 : * If already present, update it.
245 : */
246 8 : context_iterator c_itr;
247 8 : bool changed = false;
248 :
249 8 : FlagConfig flag_cfg(name, version, enabled, state, context_infos);
250 :
251 8 : std::scoped_lock lock(mutex_);
252 8 : flag_map_itr fmap_itr = flag_map_.find(name);
253 8 : if (fmap_itr != flag_map_.end()) {
254 : /**
255 : * Flag already present in FlagMap.
256 : * Check if something changed and update.
257 : */
258 2 : FlagConfig old_cfg = fmap_itr->second;
259 :
260 2 : FLAG_DEBUG("Flag " << name << "Already Present\n");
261 2 : FLAG_DEBUG(" EXISTING INFO:\n "
262 : << " Version: " << old_cfg.version()
263 : << " Enabled: " << string(old_cfg.enabled() ? "True " : "False ")
264 : << " State: " << FlagState::ToString(old_cfg.state()));
265 2 : const ContextVec c_vec = old_cfg.context_infos();
266 2 : FLAG_DEBUG("Context Info: ");
267 3 : for (c_itr = c_vec.begin(); c_itr != c_vec.end(); ++c_itr) {
268 1 : FLAG_DEBUG("Description: " << c_itr->desc
269 : << " Value: " << c_itr->value);
270 : }
271 :
272 2 : if (old_cfg != flag_cfg) {
273 2 : FLAG_DEBUG("Flag " << name << " updated\n");
274 2 : changed = true;
275 2 : fmap_itr->second = flag_cfg;
276 : } else {
277 0 : FLAG_DEBUG("No change in flag " << name << " configuration\n");
278 : }
279 2 : } else {
280 : /**
281 : * New feature flag. Insert into FlagMap
282 : */
283 6 : changed = true;
284 6 : FLAG_DEBUG("New flag configured. Name: " << name);
285 6 : flag_map_.insert(make_pair(name, flag_cfg));
286 : }
287 :
288 8 : FLAG_DEBUG(" NEW INFO:\n "
289 : << " Version: " << flag_cfg.version()
290 : << " Enabled: " << string(flag_cfg.enabled() ? "True " : "False ")
291 : << " State: " << FlagState::ToString(flag_cfg.state()));
292 8 : const ContextVec c_vec = flag_cfg.context_infos();
293 8 : FLAG_DEBUG("Context Info: ");
294 19 : for (c_itr = c_vec.begin(); c_itr != c_vec.end(); ++c_itr) {
295 11 : FLAG_DEBUG("Description: " << c_itr->desc
296 : << " Value: " << c_itr->value);
297 : }
298 :
299 : /**
300 : * New feature flag configured or change in existing flag definition.
301 : * If module is interested in this flag,
302 : * 1. invoke all callbacks registered by the module for this flag.
303 : * 2. Set enabled property of Flag in InterestMap so that when modules
304 : * using this flag query to check if it is enabled, the updated
305 : * result is returned.
306 : */
307 8 : if (changed) {
308 : bool enabled;
309 8 : pair <int_map_itr, int_map_itr> ret;
310 8 : ret = int_map_.equal_range(name);
311 8 : for (int_map_itr it = ret.first; it != ret.second; ++it) {
312 0 : enabled = false;
313 0 : Flag *f = it->second;
314 0 : enabled = IsFlagEnabled(name, f->enabled(), f->context_infos());
315 0 : f->set_enabled(enabled);
316 0 : f->InvokeCb();
317 : }
318 : }
319 8 : }
320 :
321 6 : void FlagManager::Unset(const string &name) {
322 : /**
323 : * Remove from FlagMap
324 : */
325 6 : flag_map_.erase(name);
326 :
327 : /**
328 : * Inform modules of the change
329 : */
330 6 : pair <int_map_itr, int_map_itr> ret;
331 6 : ret = int_map_.equal_range(name);
332 6 : for (int_map_itr it = ret.first; it != ret.second; ++it) {
333 0 : Flag *f = it->second;
334 0 : f->set_enabled(false);
335 0 : f->InvokeCb();
336 : }
337 6 : }
338 :
339 : /**
340 : * Check if Feature Flag is enabled given the name and
341 : * optional ContextInfo.
342 : * Feature is enabled under following conditions
343 : * 1. Feature flag present in FlagMap and is enabled OR
344 : * 2. Feature flag present in FlagMap but is not enabled
345 : * However, default value is set to enabled AND
346 : * 3. If context is provided and matches what is configured
347 : * in the FlagMap.
348 : * If feature is not present in the FlagMap meaning that user
349 : * has not configured the flag, the modules can use their own
350 : * default values.
351 : */
352 13 : bool FlagManager::IsFlagEnabled(const string &name,
353 : bool default_state,
354 : const ContextVec &c_vec) const {
355 13 : flag_map_citr fitr;
356 13 : bool result = false;
357 13 : context_iterator c_itr, f_itr;
358 :
359 13 : FLAG_DEBUG("Checking if flag with name: " << name
360 : << " is enabled for context");
361 24 : for (c_itr = c_vec.begin(); c_itr != c_vec.end(); ++c_itr) {
362 11 : FLAG_DEBUG("Description: " << c_itr->desc
363 : << " Value: " << c_itr->value);
364 : }
365 :
366 13 : fitr = flag_map_.find(name);
367 13 : if (fitr != flag_map_.end()) {
368 : /**
369 : * Get flag config
370 : */
371 12 : FlagConfig flag_cfg = fitr->second;
372 :
373 : /**
374 : * Check if Flag is enabled in config
375 : */
376 12 : if (flag_cfg.enabled()) {
377 6 : result = true;
378 : }
379 :
380 : /**
381 : * Check if context matches user config
382 : */
383 12 : const ContextVec f_vec = flag_cfg.context_infos();
384 12 : if (c_vec.empty() && !f_vec.empty()) {
385 1 : result = false;
386 : }
387 23 : for (c_itr = c_vec.begin(); c_itr != c_vec.end(); ++c_itr) {
388 11 : f_itr = find(f_vec.begin(), f_vec.end(), *c_itr);
389 11 : if (f_itr == f_vec.end()) {
390 : /**
391 : *Context does not match
392 : */
393 1 : result = false;
394 : }
395 : }
396 12 : } else {
397 : /**
398 : * Flag not present in FlagMap. Modules will use their
399 : * default state.
400 : */
401 1 : result = default_state;
402 : }
403 :
404 13 : FLAG_DEBUG("Flag enabled: " << string(result ? "True" : "False"));
405 :
406 13 : return result;
407 : }
408 :
409 5 : void FlagManager::Register(Flag *flag) {
410 5 : int_map_itr itr;
411 5 : flag_map_citr fitr;
412 :
413 5 : std::scoped_lock lock(mutex_);
414 :
415 5 : string name = flag->name();
416 :
417 5 : FLAG_DEBUG("Module interested in flag. Name: " << name <<
418 : " \nAdding to InterestMap");
419 :
420 : /**
421 : * Set value of flag if already present in FlagMap
422 : * Also invoke cb to let modules know about the presence of user
423 : * configuration for this flag
424 : */
425 5 : fitr = flag_map_.find(name);
426 5 : if (fitr != flag_map_.end()) {
427 4 : FLAG_DEBUG("Flag Name: " << name <<
428 : " already present in FlagMap");
429 4 : bool value = IsFlagEnabled(name, flag->enabled(),
430 : flag->context_infos());
431 4 : flag->set_enabled(value);
432 4 : flag->InvokeCb();
433 : }
434 :
435 : /**
436 : * Insert into InterestMap
437 : */
438 5 : itr = int_map_.insert(make_pair(name, flag));
439 5 : }
440 :
441 5 : void FlagManager::Unregister(const Flag *flag) {
442 5 : pair <int_map_itr, int_map_itr> ret;
443 5 : const string name = flag->name();
444 5 : ret = int_map_.equal_range(name);
445 :
446 5 : FLAG_DEBUG("Module not interested in flag. Name: " << name <<
447 : " \nRemoving from InterestMap");
448 :
449 5 : std::scoped_lock lock(mutex_);
450 :
451 5 : int_map_itr it = ret.first;
452 5 : while (it != ret.second) {
453 5 : if (it->second == flag) {
454 5 : int_map_.erase(it);
455 5 : break;
456 : } else {
457 0 : ++it;
458 : }
459 : }
460 5 : }
461 :
462 5 : bool FlagManager::IsRegistered(const Flag *flag) const {
463 5 : pair <int_map_const_itr, int_map_const_itr> ret;
464 :
465 5 : std::scoped_lock lock(mutex_);
466 :
467 5 : ret = int_map_.equal_range(flag->name());
468 6 : for (int_map_const_itr it = ret.first; it != ret.second; ++it) {
469 6 : if (it->second == flag) {
470 5 : return true;
471 : }
472 : }
473 0 : return false;
474 5 : }
475 :
476 10 : size_t FlagManager::GetIntMapCount() const {
477 10 : return (int_map_.size());
478 : }
479 :
480 470 : FlagConfigVec FlagManager::GetFlagInfosUnlocked() const {
481 470 : flag_map_citr fitr;
482 470 : int_map_const_itr iitr;
483 470 : FlagConfigVec flag_infos;
484 :
485 : /**
486 : * Add flag only if module is interested. This information is
487 : * available in the InterestMap.
488 : */
489 557 : for (fitr = flag_map_.begin(); fitr != flag_map_.end(); ++fitr) {
490 87 : FlagConfig flag_cfg = fitr->second;
491 87 : iitr = int_map_.find(flag_cfg.name());
492 87 : if (iitr != int_map_.end()) {
493 5 : flag_infos.push_back(flag_cfg);
494 : }
495 87 : }
496 :
497 940 : return flag_infos;
498 0 : }
499 :
500 17 : FlagConfigVec FlagManager::GetFlagInfos() const {
501 17 : std::scoped_lock lock(mutex_);
502 34 : return GetFlagInfosUnlocked();
503 17 : }
504 :
505 : } // namespace process
|