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

Generated by: LCOV version 1.14