LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/base - feature_flags.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 178 220 80.9 %
Date: 2026-11-09 02:10:49 Functions: 24 28 85.7 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14