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