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-08-03 02:19:58 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             :  */
       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

Generated by: LCOV version 1.14