Line data Source code
1 : /* 2 : * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : #include "base/connection_info.h" 5 : 6 : #include <boost/system/error_code.hpp> 7 : #include <boost/asio/ip/tcp.hpp> 8 : #include <boost/asio/ip/address.hpp> 9 : #include <boost/tuple/tuple_comparison.hpp> 10 : #include <boost/foreach.hpp> 11 : 12 : #include "base/string_util.h" 13 : #include "base/sandesh/process_info_constants.h" 14 : 15 : namespace process { 16 : 17 : using std::vector; 18 : using std::string; 19 : 20 : // ConnectionState 21 : boost::scoped_ptr<ConnectionState> ConnectionState::instance_; 22 : boost::scoped_ptr<ConnectionStateManager> ConnectionStateManager::instance_; 23 : 24 : 25 161 : ConnectionState::ConnectionState(SendUveCb send_uve_cb) : 26 161 : send_uve_cb_(send_uve_cb) { 27 161 : } 28 : 29 : // CreateInstance should be called from ConnectionStateManager::Init() 30 48 : void ConnectionState::CreateInstance(SendUveCb send_uve_cb) { 31 : // The assert is to catch errors where ConnectionState::GetInstance() 32 : // is called before ConnectionStateManager::Init() 33 48 : assert(instance_ == NULL); 34 48 : instance_.reset(new ConnectionState(send_uve_cb)); 35 48 : } 36 : 37 60810 : ConnectionState* ConnectionState::GetInstance() { 38 60810 : if (instance_ == NULL) { 39 : // This is needed to handle unit tests that do not call 40 : // ConnectionStateManager::Init() 41 113 : instance_.reset(new ConnectionState(NULL)); 42 : } 43 60810 : return instance_.get(); 44 : } 45 : 46 16848 : void ConnectionState::Update() { 47 16848 : if (!send_uve_cb_.empty()) { 48 10 : send_uve_cb_(); 49 : } 50 16848 : } 51 : 52 43528 : void ConnectionState::UpdateInternal(ConnectionType::type ctype, 53 : const string &name, ConnectionStatus::type status, 54 : const vector<Endpoint> &servers, string message) { 55 : // Populate key 56 43528 : ConnectionInfoKey key(ctype, name); 57 : // Populate info 58 43528 : ConnectionInfo info; 59 43526 : info.set_type( 60 43527 : g_process_info_constants.ConnectionTypeNames.find(ctype)->second); 61 43526 : info.set_name(name); 62 43526 : vector<string> server_addrs; 63 45466 : BOOST_FOREACH(const Endpoint &server, servers) { 64 970 : boost::system::error_code ec; 65 970 : string saddr(server.address().to_string(ec)); 66 970 : int sport(server.port()); 67 1940 : string server_address(saddr + ":" + integerToString(sport)); 68 970 : server_addrs.push_back(server_address); 69 970 : } 70 43526 : info.set_server_addrs(server_addrs); 71 43526 : info.set_status( 72 43526 : g_process_info_constants.ConnectionStatusNames.find(status)->second); 73 43526 : info.set_description(message); 74 : // Lookup connection info map 75 43528 : std::scoped_lock lock(mutex_); 76 43528 : ConnectionInfoMap::iterator it = connection_map_.find(key); 77 43528 : if (it != connection_map_.end()) { 78 : // Update 79 86651 : if (it->second.server_addrs == info.server_addrs && 80 86651 : it->second.status == info.status && 81 27420 : it->second.description == info.description) { 82 : // Do not send UVE if there is no change in the server_addrs, 83 : // status or description 84 27235 : return; 85 : } 86 16161 : it->second = info; 87 : } else { 88 : // Add 89 132 : connection_map_[key] = info; 90 : } 91 16293 : if (!send_uve_cb_.empty()) { 92 424 : send_uve_cb_(); 93 : } 94 125233 : } 95 : 96 43036 : void ConnectionState::Update(ConnectionType::type ctype, 97 : const string &name, ConnectionStatus::type status, 98 : const vector<Endpoint> &servers, string message) { 99 43036 : UpdateInternal(ctype, name, status, servers, message); 100 43036 : } 101 : 102 492 : void ConnectionState::Update(ConnectionType::type ctype, 103 : const string &name, ConnectionStatus::type status, 104 : Endpoint server, string message) { 105 492 : UpdateInternal(ctype, name, status, boost::assign::list_of(server), 106 : message); 107 492 : } 108 : 109 5 : void ConnectionState::Delete(ConnectionType::type ctype, 110 : const string &name) { 111 : // Construct key 112 5 : ConnectionInfoKey key(ctype, name); 113 : // Delete 114 5 : std::scoped_lock lock(mutex_); 115 5 : connection_map_.erase(key); 116 5 : if (!send_uve_cb_.empty()) { 117 5 : send_uve_cb_(); 118 : } 119 5 : } 120 : 121 453 : vector<ConnectionInfo> ConnectionState::GetInfosUnlocked() const { 122 453 : vector<ConnectionInfo> infos; 123 453 : for (ConnectionInfoMap::const_iterator it = connection_map_.begin(); 124 1367 : it != connection_map_.end(); it++) { 125 914 : infos.push_back(it->second); 126 : } 127 453 : return infos; 128 0 : } 129 : 130 0 : vector<ConnectionInfo> ConnectionState::GetInfos() const { 131 0 : std::scoped_lock lock(mutex_); 132 0 : return GetInfosUnlocked(); 133 0 : } 134 : 135 421 : void GetProcessStateCb(const vector<ConnectionInfo> &cinfos, 136 : ProcessState::type &state, string &message, 137 : const vector<ConnectionTypeName> &expected_connections) { 138 : // Determine if the number of connections is as expected. 139 421 : size_t num_connections(cinfos.size()); 140 421 : if (num_connections != expected_connections.size()) { 141 298 : GetConnectionInfoMessage(cinfos, expected_connections, message); 142 298 : state = ProcessState::NON_FUNCTIONAL; 143 298 : return; 144 : } 145 : string cup(g_process_info_constants.ConnectionStatusNames. 146 123 : find(ConnectionStatus::UP)->second); 147 123 : bool is_cup = true; 148 : // Iterate to determine process connectivity status 149 123 : for (vector<ConnectionInfo>::const_iterator it = cinfos.begin(); 150 489 : it != cinfos.end(); it++) { 151 366 : const ConnectionInfo &cinfo(*it); 152 366 : const string &conn_status(cinfo.get_status()); 153 366 : if (conn_status != cup) { 154 100 : is_cup = false; 155 100 : if (message.empty()) { 156 72 : message = cinfo.get_type(); 157 : } else { 158 28 : message += ", " + cinfo.get_type(); 159 : } 160 100 : const string &name(cinfo.get_name()); 161 100 : if (!name.empty()) { 162 30 : message += ":" + name; 163 : } 164 : } 165 : } 166 : // All critical connections are in good condition. 167 123 : if (is_cup) { 168 51 : state = ProcessState::FUNCTIONAL; 169 : } else { 170 72 : state = ProcessState::NON_FUNCTIONAL; 171 72 : message += " connection down"; 172 : } 173 123 : return; 174 123 : } 175 : 176 : // Custom find function to compare ConnectionInfo and 177 : // expected connection structures 178 : struct CompareConnections : public std::unary_function<ConnectionTypeName, 179 : bool> { 180 : ConnectionTypeName expected_connection_; 181 876 : explicit CompareConnections(const ConnectionTypeName exp_connection) : 182 876 : expected_connection_(exp_connection) {} 183 1230 : bool operator() (const ConnectionInfo cinfo) { 184 1707 : if (expected_connection_.first == cinfo.get_type() && 185 477 : expected_connection_.second == cinfo.get_name()) { 186 474 : return true; 187 : } else { 188 756 : return false; 189 : } 190 : } 191 : }; 192 : 193 298 : void GetConnectionInfoMessage(const vector<ConnectionInfo> &cinfos, 194 : const vector<ConnectionTypeName> &expected_connections, 195 : string &message) { 196 298 : size_t num_connections(cinfos.size()); 197 596 : message = "Number of connections:" + integerToString(num_connections) + 198 894 : ", Expected:" + integerToString(expected_connections.size()); 199 298 : if (num_connections > expected_connections.size()) { 200 1 : size_t i = 0; 201 1 : message += " Extra: "; 202 : // find the extra connection 203 1 : for (vector<ConnectionInfo>::const_iterator it = cinfos.begin(); 204 4 : it != cinfos.end(); it++) { 205 3 : const ConnectionInfo &cinfo(*it); 206 3 : ConnectionTypeName con_info(cinfo.get_type(), cinfo.get_name()); 207 3 : vector<ConnectionTypeName>::const_iterator position; 208 3 : position = std::find(expected_connections.begin(), 209 : expected_connections.end(), con_info); 210 3 : if (position == expected_connections.end()) { 211 1 : i++; 212 1 : message += con_info.first; 213 1 : if (!con_info.second.empty()) { 214 1 : message += ":" + con_info.second; 215 : } 216 1 : if (i != num_connections-expected_connections.size()) { 217 0 : message += ","; 218 : } 219 : } 220 3 : } 221 : } else { 222 : // find the missing connection 223 297 : size_t i = 0; 224 297 : message += " Missing: "; 225 297 : for (vector<ConnectionTypeName>::const_iterator it = 226 1470 : expected_connections.begin(); it != expected_connections.end(); 227 876 : it++) { 228 876 : vector<ConnectionInfo>::const_iterator position; 229 876 : position = std::find_if(cinfos.begin(), cinfos.end(), 230 1752 : CompareConnections(*it)); 231 : // If connection is not found in cinfo, its a missing 232 : // connection 233 876 : if (position == cinfos.end()) { 234 402 : i++; 235 402 : message += it->first; 236 402 : if (!it->second.empty()) { 237 161 : message += ":" + it->second; 238 : } 239 402 : if (i != expected_connections.size() - cinfos.size()) { 240 105 : message += ","; 241 : } 242 : } 243 : } 244 : } 245 298 : } 246 : 247 : vector<FlagInfo> 248 453 : ConnectionStateManager::GetFlagInfos(const FlagConfigVec &flag_infos) { 249 453 : FlagInfo info; 250 453 : vector<FlagInfo> infos; 251 453 : ContextInfo c_info; 252 453 : vector<ContextInfo> c_infos; 253 : FlagState state; 254 453 : ContextVec c_vec; 255 453 : context_iterator c_itr; 256 453 : for (flag_cfg_itr it = flag_infos.begin(); it != flag_infos.end(); it++) { 257 0 : info.set_name(it->name()); 258 0 : info.set_version(it->version()); 259 0 : info.set_enabled(it->enabled()); 260 0 : info.set_state(state.ToString(it->state())); 261 0 : c_vec = it->context_infos(); 262 0 : for (c_itr = c_vec.begin(); c_itr != c_vec.end(); c_itr++) { 263 0 : c_info.desc = c_itr->desc; 264 0 : c_info.value = c_itr->value; 265 0 : c_infos.push_back(c_info); 266 : } 267 0 : info.set_context_infos(c_infos); 268 0 : infos.push_back(info); 269 : } 270 906 : return infos; 271 453 : } 272 : 273 : } // namespace process