LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-analytics/contrail-collector - redis_connection.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 146 232 62.9 %
Date: 2026-08-03 02:19:58 Functions: 10 14 71.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "redis_connection.h"
       6             : 
       7             : #include <cstdlib>
       8             : #include <mutex>
       9             : 
      10             : #include <boost/bind/bind.hpp>
      11             : #include "base/util.h"
      12             : #include "base/address_util.h"
      13             : #include "base/logging.h"
      14             : #include "base/parse_object.h"
      15             : #include "hiredis/hiredis.h"
      16             : #include "hiredis/hiredis_ssl.h"
      17             : #include "hiredis/base64.h"
      18             : #include "hiredis/boostasio.hpp"
      19             : 
      20             : using std::string;
      21             : using std::vector;
      22             : using namespace boost::placeholders;
      23             : 
      24             : const int RedisAsyncConnection::RedisReconnectTime;
      25             : RedisAsyncConnection::RAC_CbFnsMap RedisAsyncConnection::rac_cb_fns_map_;
      26             : std::mutex RedisAsyncConnection::rac_cb_fns_map_mutex_;
      27             : 
      28         220 : RedisAsyncConnection::RedisAsyncConnection(EventManager *evm, const std::string & redis_ip,
      29             :         unsigned short redis_port, ClientConnectCbFn client_connect_cb,
      30             :         ClientDisconnectCbFn client_disconnect_cb,
      31             :         const bool redis_ssl_enable, const string & redis_keyfile,
      32         220 :         const string & redis_certfile, const string & redis_ca_cert) :
      33         220 :     evm_(evm),
      34         220 :     hostname_(redis_ip),
      35         220 :     port_(redis_port),
      36         220 :     redis_ssl_enable_(redis_ssl_enable),
      37         220 :     redis_keyfile_(redis_keyfile),
      38         220 :     redis_certfile_(redis_certfile),
      39         220 :     redis_ca_cert_(redis_ca_cert),
      40         220 :     callDisconnected_(0),
      41         220 :     callFailed_(0),
      42         220 :     callSucceeded_(0),
      43         220 :     callbackNull_(0),
      44         220 :     callbackFailed_(0),
      45         220 :     callbackSucceeded_(0),
      46         220 :     context_(NULL),
      47         220 :     state_(REDIS_ASYNC_CONNECTION_INIT),
      48         220 :     reconnect_timer_(*evm->io_service()),
      49         220 :     client_connect_cb_(client_connect_cb),
      50         660 :     client_disconnect_cb_(client_disconnect_cb) {
      51         220 :     boost::system::error_code ec;
      52             :     boost::asio::ip::address redis_addr(
      53         220 :         AddressFromString(hostname_, &ec));
      54         220 :     endpoint_ = boost::asio::ip::tcp::endpoint(redis_addr, redis_port);
      55         220 : }
      56             : 
      57           0 : RedisAsyncConnection::~RedisAsyncConnection() {
      58           0 :     boost::system::error_code ec;
      59             : 
      60           0 :     if (client_connect_cb_)
      61           0 :         client_connect_cb_ = NULL;
      62             : 
      63           0 :     if (client_disconnect_cb_)
      64           0 :         client_disconnect_cb_ = NULL;
      65             : 
      66           0 :     if (context_) {
      67             :       {
      68           0 :         std::scoped_lock lock(rac_cb_fns_map_mutex_);
      69             : 
      70             :         RedisAsyncConnection::RAC_CbFnsMap& fns_map = 
      71           0 :             RedisAsyncConnection::rac_cb_fns_map();
      72           0 :         RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(context_);
      73           0 :         assert(it != fns_map.end());
      74           0 :         fns_map.erase(it);
      75           0 :       }
      76           0 :       redisAsyncFree(context_);
      77             :     }    
      78           0 :     reconnect_timer_.cancel(ec);
      79           0 : }
      80             : 
      81          35 : void RedisAsyncConnection::RAC_Reconnect(const boost::system::error_code &error) {
      82          35 :     if (error) {
      83           0 :         LOG(INFO, "RAC_Reconnect error: " << error.message());
      84           0 :         if (error.value() != boost::system::errc::operation_canceled) {
      85           0 :             LOG(INFO, "OpServerProxy::OpServerImpl::RAC_Reconnect error: "
      86             :                     << error.category().name()
      87             :                     << " " << error.message());
      88             :         } else {
      89           0 :             return;
      90             :         }
      91             :     } else {
      92          35 :         LOG(INFO, "RedisAsyncConnection::RAC_Reconnect initiated " << this);
      93             :     }
      94             : 
      95          35 :     if (!RAC_Connect()) {
      96           0 :         assert(0);
      97             :     }
      98             : }
      99             : 
     100         270 : void RedisAsyncConnection::RAC_ConnectCallbackProcess(const struct redisAsyncContext *c, int status) {
     101         270 :     LOG(DEBUG, "RAC_Connect status: " << status << " " << this);
     102         270 :     if (status != REDIS_OK) {
     103          40 :         if (context_) {
     104          40 :             std::scoped_lock lock(rac_cb_fns_map_mutex_);
     105             : 
     106             :             RedisAsyncConnection::RAC_CbFnsMap& fns_map = 
     107          40 :                 RedisAsyncConnection::rac_cb_fns_map();
     108          40 :             RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(context_);
     109          40 :             if (it != fns_map.end()) {
     110          40 :                 fns_map.erase(it);
     111             :             } 
     112          40 :             context_ = NULL;
     113          40 :             client_.reset();
     114          40 :         }
     115             : 
     116          40 :         boost::system::error_code ec;
     117          40 :         reconnect_timer_.expires_from_now(boost::posix_time::seconds(RedisAsyncConnection::RedisReconnectTime), ec);
     118          40 :         reconnect_timer_.async_wait(boost::bind(&RedisAsyncConnection::RAC_Reconnect, this, boost::asio::placeholders::error));
     119          40 :         return;
     120             :     }
     121         230 :     state_ = REDIS_ASYNC_CONNECTION_CONNECTED;
     122         230 :     LOG(DEBUG, "Connected to REDIS...\n");
     123             : 
     124         230 :     if (client_connect_cb_)
     125         230 :         client_connect_cb_();
     126             : }
     127             : 
     128           0 : void RedisAsyncConnection::RAC_StatUpdate(const redisReply *reply) {
     129           0 :     if (reply == NULL) {
     130           0 :         callbackNull_++;
     131           0 :         return;
     132             :     }
     133           0 :     if (reply->type == REDIS_REPLY_ERROR) {
     134           0 :         callbackFailed_++;
     135             :     }  else {
     136           0 :         callbackSucceeded_++;
     137             :     }
     138             : }
     139             : 
     140         270 : void RedisAsyncConnection::RAC_ConnectCallback(const struct redisAsyncContext *c, int status) {
     141         270 :     RedisAsyncConnection::RAC_ConnectCbFn fn;
     142             : 
     143             :       {
     144         270 :         std::scoped_lock lock(rac_cb_fns_map_mutex_);
     145         270 :         RedisAsyncConnection::RAC_CbFnsMap& fns_map = RedisAsyncConnection::rac_cb_fns_map();
     146         270 :         RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(c);
     147         270 :         if (it == fns_map.end())
     148           0 :             assert(0);
     149             : 
     150         270 :         RedisAsyncConnection::RAC_CbFns *fns = it->second;
     151         270 :         fn = fns->connect_cbfn_;
     152         270 :         fns->connect_cbfn_ = NULL;
     153         270 :       }
     154             : 
     155         270 :     if (!fn) {
     156           0 :         assert(0);
     157             :     }
     158         270 :     (fn)(c, status);
     159         270 : }
     160             : 
     161          15 : void RedisAsyncConnection::RAC_DisconnectCallbackProcess(const struct redisAsyncContext *c, int status) {
     162          15 :       LOG(DEBUG, "RAC_Disconnect status: " << status << " " << this);
     163             :       {
     164          15 :         std::scoped_lock lock(rac_cb_fns_map_mutex_);
     165          15 :         RedisAsyncConnection::RAC_CbFnsMap& fns_map = RedisAsyncConnection::rac_cb_fns_map();
     166          15 :         RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(context_);
     167          15 :         if (it != fns_map.end())
     168          15 :             fns_map.erase(it);
     169          15 :         state_ = REDIS_ASYNC_CONNECTION_DISCONNECTED;
     170          15 :         context_ = NULL;
     171          15 :         client_.reset();
     172          15 :       }
     173             : 
     174          15 :     if (client_disconnect_cb_)
     175          15 :         client_disconnect_cb_();
     176          15 : }
     177             : 
     178          15 : void RedisAsyncConnection::RAC_DisconnectCallback(const struct redisAsyncContext *c, int status) {
     179          15 :     RedisAsyncConnection::RAC_DisconnectCbFn fn;
     180             : 
     181             :       {
     182          15 :         std::scoped_lock lock(rac_cb_fns_map_mutex_);
     183          15 :         RedisAsyncConnection::RAC_CbFnsMap& fns_map = RedisAsyncConnection::rac_cb_fns_map();
     184          15 :         RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(c);
     185          15 :         if (it == fns_map.end()) {
     186           0 :             return;
     187             :         }
     188             : 
     189          15 :         RedisAsyncConnection::RAC_CbFns *fns = it->second;
     190          15 :         fn = fns->disconnect_cbfn_;
     191          15 :         fns->disconnect_cbfn_ = NULL;
     192          15 :       }
     193             : 
     194          15 :     if (!fn) {
     195           0 :         assert(0);
     196             :     }
     197             : 
     198          15 :     (fn)(c, status);
     199          15 : }
     200             : 
     201         270 : bool RedisAsyncConnection::RAC_Connect(void) {
     202         270 :     std::scoped_lock lock(mutex_);
     203             : 
     204         270 :     assert(!context_);
     205         270 :     context_ = redisAsyncConnect(hostname_.c_str(), port_);
     206         270 :     if (context_->err) {
     207           0 :         LOG(DEBUG, "RAC_Connect: redisAsyncConnect() failed:" << context_->errstr);
     208           0 :         boost::system::error_code ec;
     209           0 :         reconnect_timer_.expires_from_now(boost::posix_time::seconds(RedisAsyncConnection::RedisReconnectTime), ec);
     210           0 :         reconnect_timer_.async_wait(boost::bind(&RedisAsyncConnection::RAC_Reconnect, this, boost::asio::placeholders::error));
     211           0 :         context_ = NULL;
     212           0 :         return true;
     213             :     }
     214             : 
     215             :     /* Secure the connection if SSL enabled */
     216         270 :     if (redis_ssl_enable_) {
     217           0 :         redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
     218           0 :         redisSSLContext *ssl_ctx = redisCreateSSLContext(
     219             :             redis_ca_cert_.c_str(),
     220             :             NULL,
     221             :             redis_certfile_.c_str(),
     222             :             redis_keyfile_.c_str(),
     223             :             "sni",
     224             :             &ssl_error);
     225           0 :         if (!ssl_ctx || ssl_error != REDIS_SSL_CTX_NONE) {
     226           0 :             LOG(DEBUG, "RAC_Connect: redisCreateSSLContext() failed: "
     227             :                 << redisSSLContextGetError(ssl_error));
     228           0 :             if (ssl_ctx) redisFreeSSLContext(ssl_ctx);
     229           0 :             boost::system::error_code ec;
     230           0 :             reconnect_timer_.expires_from_now(
     231           0 :                          boost::posix_time::seconds(RedisAsyncConnection::RedisReconnectTime), ec);
     232           0 :             reconnect_timer_.async_wait(boost::bind(&RedisAsyncConnection::RAC_Reconnect, this,
     233             :                                    boost::asio::placeholders::error));
     234           0 :             context_ = NULL;
     235           0 :             return true;
     236             :         }
     237             : 
     238           0 :         int rc = redisInitiateSSLWithContext(&context_->c, ssl_ctx);
     239           0 :         if (rc != REDIS_OK) {
     240           0 :             LOG(DEBUG, "RAC_Connect: redisInitiateSSLWithContext() failed: "
     241             :                 << (&context_->c)->errstr);
     242           0 :             redisFreeSSLContext(ssl_ctx);
     243           0 :             boost::system::error_code ec;
     244           0 :             reconnect_timer_.expires_from_now(
     245           0 :                          boost::posix_time::seconds(RedisAsyncConnection::RedisReconnectTime), ec);
     246           0 :             reconnect_timer_.async_wait(boost::bind(&RedisAsyncConnection::RAC_Reconnect, this,
     247             :                                    boost::asio::placeholders::error));
     248           0 :             context_ = NULL;
     249           0 :             return true;
     250             :         }
     251           0 :         redisFreeSSLContext(ssl_ctx);
     252             :     }
     253             : 
     254         270 :     client_.reset(new redisBoostClient(*evm_->io_service(), context_, mutex_));
     255             : 
     256         270 :     std::scoped_lock fns_lock(rac_cb_fns_map_mutex_);
     257             : 
     258         270 :     assert(redisAsyncSetConnectCallback(context_, RedisAsyncConnection::RAC_ConnectCallback) == REDIS_OK);
     259         270 :     RedisAsyncConnection::RAC_CbFnsMap& fns_map = RedisAsyncConnection::rac_cb_fns_map();
     260         270 :     RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(context_);
     261         270 :     if (it == fns_map.end()) {
     262         270 :         const redisAsyncContext *c_context = context_;
     263         270 :         it = (fns_map.insert(c_context, new RAC_CbFns)).first;
     264             :     } else {
     265           0 :         assert(0);
     266             :     }
     267         270 :     it->second->stat_cbfn_ = boost::bind(&RedisAsyncConnection::RAC_StatUpdate, this, _1);
     268         270 :     it->second->connect_cbfn_ = boost::bind(&RedisAsyncConnection::RAC_ConnectCallbackProcess, this, _1, _2);
     269             : 
     270         270 :     assert(redisAsyncSetDisconnectCallback(context_, RedisAsyncConnection::RAC_DisconnectCallback) == REDIS_OK);
     271         270 :     it->second->disconnect_cbfn_ = boost::bind(&RedisAsyncConnection::RAC_DisconnectCallbackProcess, this, _1, _2);
     272             : 
     273         270 :     state_ = REDIS_ASYNC_CONNECTION_PENDING;
     274         270 :     return true;
     275         270 : }
     276             : 
     277        3608 : void RedisAsyncConnection::RAC_AsyncCmdCallback(redisAsyncContext *c, void *r, void *privdata) {
     278        3608 :     ClientAsyncCmdCbFn cbfn;
     279        3608 :     RAC_StatCbFn stat_fn;
     280             :     {
     281        3608 :         std::scoped_lock lock(rac_cb_fns_map_mutex_);
     282        3608 :         RedisAsyncConnection::RAC_CbFnsMap& fns_map = RedisAsyncConnection::rac_cb_fns_map();
     283        3608 :         RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(c);
     284        3608 :         if (it == fns_map.end()) {
     285           0 :             return;
     286             :         }
     287        3608 :         cbfn = it->second->client_async_cmd_cbfn_;
     288        3608 :         stat_fn = it->second->stat_cbfn_;
     289        3608 :     }
     290        3608 :     if (cbfn) {
     291        3608 :         (cbfn)(c, r, privdata);
     292        3608 :         return;
     293             :     }
     294             : 
     295           0 :     redisReply *reply = (redisReply*)r;
     296           0 :     assert(stat_fn);
     297           0 :     (stat_fn)(reply);
     298             : 
     299             : #if 0
     300             : 
     301             :     if (reply->type == REDIS_REPLY_ARRAY) {
     302             :         LOG(DEBUG, __func__ << "REDIS_REPLY_ARRAY == " << reply->elements);
     303             :         int i;
     304             :         for (i = 0; i < (int)reply->elements; i++) {
     305             :             if (reply->element[i]->type == REDIS_REPLY_STRING) {
     306             :                 LOG(DEBUG, __func__ << "Element" << i << "== " << reply->element[i]->str);
     307             :             } else {
     308             :                 LOG(DEBUG, __func__ << "Element" << i << " type == " << reply->element[i]->type);
     309             :             }
     310             :         }
     311             :     } else if (reply->type == REDIS_REPLY_STRING) {
     312             :         LOG(DEBUG, __func__ << "REDIS_REPLY_STRING == " << reply->str);
     313             :     } else if (reply->type == REDIS_REPLY_INTEGER) {
     314             :         LOG(DEBUG, __func__ << "REDIS_REPLY_INTEGER == " << reply->type);
     315             :     } else {
     316             :         LOG(DEBUG, __func__ << "reply->type == " << reply->type);
     317             :     }
     318             : #endif
     319        7216 : }
     320             : 
     321         460 : bool RedisAsyncConnection::SetClientAsyncCmdCb(ClientAsyncCmdCbFn cb_fn) {
     322         460 :     std::scoped_lock lock(rac_cb_fns_map_mutex_);
     323         460 :     RedisAsyncConnection::RAC_CbFnsMap& fns_map = rac_cb_fns_map();
     324         460 :     RedisAsyncConnection::RAC_CbFnsMap::iterator it = fns_map.find(context_);
     325             : 
     326         460 :     if (it == fns_map.end()) {
     327           0 :         assert(0);
     328             :     } else {
     329         460 :         it->second->client_async_cmd_cbfn_ = cb_fn;
     330             :     }
     331         460 :     return true;
     332         460 : }
     333             : 
     334             : 
     335             : 
     336        3611 : bool RedisAsyncConnection::RedisAsyncArgCmd(void *rpi,
     337             :         const vector<string> &args) {
     338             : 
     339        3611 :     std::scoped_lock lock(mutex_);
     340             : 
     341        3651 :     if (state_ != REDIS_ASYNC_CONNECTION_CONNECTED) {
     342           0 :         callDisconnected_++;
     343           0 :         return false;
     344             :     }
     345             : 
     346        3651 :     int argc = args.size();
     347        3651 :     const char** argv = new const char* [argc];
     348       17181 :     for (uint i=0; i < args.size(); i++) {
     349       13531 :         argv[i] = args[i].c_str();
     350             :     }
     351        3648 :     bool status = false;
     352             :     int ret;
     353             : 
     354        3648 :     ret = redisAsyncCommandArgv(context_,
     355             :             RedisAsyncConnection::RAC_AsyncCmdCallback,
     356             :             rpi,
     357             :             argc,
     358             :             argv,
     359             :             NULL);
     360             : 
     361        3651 :     delete[] argv;
     362             : 
     363        3651 :     if (REDIS_ERR == ret) {
     364           0 :         LOG(INFO, "Could NOT apply " << args[0] << " to Redis : ");
     365           0 :         callFailed_++;
     366             :     } else {
     367        3651 :         status = true;
     368        3651 :         callSucceeded_++;
     369             :     }
     370        3651 :     return status;
     371        3651 : }
     372             : 
     373             : 
     374           0 : bool RedisAsyncConnection::RedisAsyncCommand(void *rpi, const char *format, ...) {
     375           0 :     std::scoped_lock lock(mutex_);
     376             : 
     377           0 :     if (state_ != REDIS_ASYNC_CONNECTION_CONNECTED) {
     378           0 :         callDisconnected_++;
     379           0 :         return false;
     380             :     }
     381             : 
     382           0 :     bool status = false;
     383             :     int ret;
     384             :     va_list ap; 
     385           0 :     va_start(ap,format);
     386             : 
     387           0 :     ret = redisvAsyncCommand(context_,
     388             :             RedisAsyncConnection::RAC_AsyncCmdCallback,
     389             :             rpi,
     390             :             format,
     391             :             ap);
     392             : 
     393           0 :     if (REDIS_ERR == ret) {
     394           0 :         LOG(INFO, "Could NOT apply " << format << " to Redis : ");
     395           0 :         callFailed_++;
     396             :     } else {
     397           0 :         status = true;
     398           0 :         callSucceeded_++;
     399             :     }
     400           0 :     va_end(ap);
     401           0 :     return status;
     402           0 : }

Generated by: LCOV version 1.14