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