00001 #ifndef NoXmlTreeReader
00002 #include "StDbServiceBroker.h"
00003 #include "ChapiStringUtilities.h"
00004 #include "mysql.h"
00005 #include "math.h"
00006 #include <string.h>
00007 #include <libxml/nanohttp.h>
00008 #include <sys/stat.h>
00009 #include <sys/types.h>
00010 #ifndef __STDB_STANDALONE__
00011 #include "StMessMgr.h"
00012 #else
00013 #define LOG_DEBUG cout
00014 #define LOG_INFO cout
00015 #define LOG_WARN cout
00016 #define LOG_ERROR cerr
00017 #define LOG_FATAL cerr
00018 #define LOG_QA cout
00019 #define endm "\n"
00020 #endif
00021 #include <algorithm>
00022 using namespace std;
00023 using namespace chapi_string_utilities;
00024
00025 typedef vector<string>::const_iterator VCI;
00026
00027 using st_db_service_broker::MyScatalogVersion;
00028 using st_db_service_broker::NO_ERROR;
00029 using st_db_service_broker::NO_USER;
00030 using st_db_service_broker::NO_DOMAIN;
00031 using st_db_service_broker::NO_HOSTS;
00032 using st_db_service_broker::NightBegins;
00033 using st_db_service_broker::NightEnds;
00034
00035 using stl_xml_tree::sep;
00036
00037 string nn[] =
00038 {
00039 "Scatalog",
00040 "Site",
00041 "Server",
00042 "Host",
00043 "Access"
00044 };
00045
00046 enum
00047 {
00048 SCATALOG,
00049 SITE,
00050 SERVER,
00051 HOST,
00052 ACCESS
00053 };
00054
00055 string NAME = "name";
00056 string PORT = "port";
00057 string USER = "user";
00058 string SCOPE = "scope";
00059 string WHEN_ACTIVE = "whenActive";
00060 string ACCESS_MODE = "accessMode";
00061 string WRITER = "writer";
00062 string POWER = "machinePower";
00063 string CAP = "cap";
00064
00065 static MYSQL *conn;
00066
00067 char* Socket = 0;
00068
00069
00071 StDbServiceBroker::StDbServiceBroker(const string xmlbase) :
00072 MyHostList(vector<ChapiDbHost>()),
00073 MyStatus(st_db_service_broker::NO_ERROR)
00074 {
00075 char* whoami = getenv("USER");
00076 if (!whoami) whoami = getenv("LOGNAME");
00077 if (!whoami)
00078 {
00079 MyStatus = NO_USER;
00080 return;
00081 }
00082
00083 const char* access_mode = getenv("DB_ACCESS_MODE");
00084 if (!access_mode)
00085 {
00086 access_mode = "read";
00087 }
00088
00089 StlXmlTree* f = new StlXmlTree();
00090 string ScatalogKey = sep+nn[SCATALOG];
00091 f->InsertKeyValuePair(ScatalogKey, MyScatalogVersion);
00092
00093 string QualifiedScatalog = sep + StlXmlTree::QualifyParent(nn[SCATALOG],MyScatalogVersion);
00094 string ServerKey = QualifiedScatalog + sep + nn[SERVER];
00095
00096 struct tm *tp;
00097 time_t timeNow;
00098 timeNow = time(NULL);
00099 tp = localtime(&timeNow);
00100 short hour = tp->tm_hour;
00101 string DayOrNight;
00102 if (hour<NightBegins && hour >NightEnds)
00103 {
00104 DayOrNight = "day";
00105 }
00106 else
00107 {
00108 DayOrNight = "night";
00109 }
00110
00111 string ServerAttr = USER+"="+(string)whoami+";"+WHEN_ACTIVE+"="+DayOrNight+";"+ACCESS_MODE+"="+access_mode;
00112 if (strcmp(whoami,"starreco")==0)
00113 {
00114 ServerAttr = SCOPE+"=Production;"+ServerAttr;
00115 }
00116 else
00117 {
00118 ServerAttr = SCOPE+"=Analysis;"+ServerAttr;
00119 }
00120 f->InsertKeyValuePair(ServerKey, ServerAttr);
00121
00122 #ifdef DEBUG
00123 LOG_INFO << " Filter XML "<<endm;
00124 f->ShowTree();
00125 #endif
00126
00127 ParsedXml = StlXmlTree(xmlbase,f);
00128 if (ParsedXml.GetStatus()==stl_xml_tree::NO_XML_BASE)
00129 {
00130 MyStatus = st_db_service_broker::NO_XML_BASE;
00131 LOG_WARN <<"StDbServiceBroker::StDbServiceBroker: no XML description of services found "<<endm;
00132 }
00133 if (ParsedXml.GetStatus()==stl_xml_tree::BAD_XML)
00134 {
00135 MyStatus = st_db_service_broker::BAD_XML;
00136 LOG_WARN <<"StDbServiceBroker::StDbServiceBroker: malformed/incorrect XML description of services found "<<endm;
00137 }
00138
00139 #ifdef DEBUG
00140 LOG_INFO << " Parsed XML "<<endm;
00141 ParsedXml.ShowTree();
00142 #endif
00143
00144 xmlCleanupParser();
00145 delete f;
00146 FormHostList();
00147 }
00149 StDbServiceBroker::StDbServiceBroker
00150 (const string xmlbase, const string xmlfilter) :
00151 MyHostList(vector<ChapiDbHost>()),
00152 MyStatus(st_db_service_broker::NO_ERROR)
00153 {
00154 StlXmlTree* f = new StlXmlTree(xmlfilter);
00155 f->ShowTree();
00156 ParsedXml = StlXmlTree(xmlbase,f);
00157
00158 if (ParsedXml.GetStatus()==stl_xml_tree::NO_XML_BASE)
00159 {
00160 MyStatus = st_db_service_broker::NO_XML_BASE;
00161 LOG_WARN <<"StDbServiceBroker::StDbServiceBroker: no XML description of services found "<<endm;
00162 }
00163 if (ParsedXml.GetStatus()==stl_xml_tree::BAD_XML)
00164 {
00165 MyStatus = st_db_service_broker::BAD_XML;
00166 LOG_WARN <<"StDbServiceBroker::StDbServiceBroker: malformed/incorrect XML description of services found "<<endm;
00167 }
00168
00169 ParsedXml.ShowTree();
00170 xmlCleanupParser();
00171 delete f;
00172 FormHostList();
00173 }
00175 void StDbServiceBroker::DoLoadBalancing()
00176 {
00177 if (MyStatus!=st_db_service_broker::NO_ERROR)
00178 {
00179 LOG_ERROR << " StDbServiceBroker::DoLoadBalancing() errors" <<MyStatus<<endm;
00180 }
00181 #ifdef DEBUG
00182 PrintHostList();
00183 #endif
00184 bool host_found = false;
00185 const int MAX_COUNT = 500;
00186 for (int i = 0; i < MAX_COUNT; i++) {
00187 if (!RecommendHost()) {
00188 host_found = true;
00189 break;
00190 }
00191 LOG_WARN << "Load Balancing attempt No " << i << ". Scanned all hosts in a Load Balancer list, no usable hosts found. Will try again in 60 seconds." << endm;
00192 sleep(60);
00193 }
00194 if (!host_found) {
00195 LOG_WARN << "Tried to find a host for " << MAX_COUNT << " times, will abort now." << endm;
00196 exit(0);
00197 }
00198
00199 #ifdef DEBUG
00200 cout << " go to "<<GiveHostName()<<" port "<<GiveHostPort()<<"\n";
00201 #endif
00202 }
00204 void StDbServiceBroker::FormHostList()
00205 {
00206
00207
00208
00209
00210
00211
00212
00213 MyHostList.clear();
00214 string key = StlXmlTree::MakeKey("",nn[SCATALOG]);
00215 vector<string> versions = ParsedXml.LookUpValueByKey(key);
00216
00217 if (versions.size()!=1) return;
00218
00219 string my_version = versions[0];
00220
00221 vector<string> services = ParsedXml.LookUpValueByKey(key,my_version,nn[SERVER]);
00222
00223 for (VCI ii = services.begin(); ii!=services.end(); ++ii)
00224 {
00225 vector<string> hosts = ParsedXml.LookUpValueByKey(key,*ii,nn[HOST]);
00226
00227 for (VCI iii = hosts.begin(); iii!=hosts.end(); ++iii)
00228 {
00229 map<string,string> host_data = StlXmlTree::ParseAttributeString(*iii);
00230
00231 int port;
00232 if (!from_string<int>(port,host_data[PORT],std::dec))
00233 {
00234
00235 port = DefaultPort;
00236 }
00237
00238 double power;
00239 short cap;
00240
00241 if (!from_string<double>(power,host_data[POWER],std::dec))
00242 {
00243
00244 #ifdef DEBUG
00245 LOG_ERROR << "StDbServiceBroker::FormHostList(): non-numeric port, using default power for host "<<*iii<<endm;
00246 #endif
00247 power = DefaultPower;
00248 }
00249
00250
00251 if (!from_string<short>(cap,host_data[CAP],std::dec))
00252 {
00253
00254 #ifdef DEBUG
00255 LOG_ERROR << "StDbServiceBroker::FormHostList(): non-numeric cap, using default cap for host "<<*iii<<endm;
00256 #endif
00257 cap = DefaultCap;
00258 }
00259
00260 ChapiDbHost host_entry = ChapiDbHost(host_data[NAME],port,power,cap);
00261 MyHostList.push_back(host_entry);
00262 }
00263 cut_string_after_sub(key,">");
00264 cut_string_after_sub(key,"(");
00265 }
00266
00267 #ifdef DEBUG
00268 PrintHostList();
00269 #endif
00270
00271 if (MyHostList.size()==0)
00272 {
00273 MyStatus = NO_HOSTS;
00274 LOG_DEBUG<<" StDbServiceBroker::RecommendHost() will have no hosts to choose from !"<<endm;
00275 return;
00276 }
00277
00278 }
00280 void StDbServiceBroker::PrintHostList()
00281 {
00282 LOG_DEBUG << " MyHostList contains:"<<endm;
00283 for (vector<ChapiDbHost>::const_iterator i = MyHostList.begin(); i!=MyHostList.end(); ++i)
00284 {
00285 LOG_DEBUG << (*i).HostName << endm;
00286 }
00287 }
00289 int StDbServiceBroker::RecommendHost()
00290 {
00291 double dproc_min = HUGE_VAL;
00292
00293
00294 MyBestHost = MyHostList.end();
00295
00296 if (MyHostList.size()==1)
00297 {
00298 MyBestHost = MyHostList.begin();
00299 return 0;
00300 }
00301
00302 srand ( unsigned ( time (NULL) ) );
00303 random_shuffle( MyHostList.begin(), MyHostList.end() );
00304
00305 for (vector<ChapiDbHost>::const_iterator I=MyHostList.begin(); I!=MyHostList.end(); ++I)
00306 {
00307 conn = mysql_init(0);
00308
00309 if (conn==0)
00310 {
00311 LOG_WARN << "StDbServiceBroker::RecommendHost() mysql_init(0) failed "<<endm;
00312 continue;
00313 }
00314
00315 conn->options.connect_timeout = 30;
00316
00317 if (mysql_real_connect
00318 (conn,((*I).HostName).c_str(), "loadbalancer","lbdb","test",(*I).Port,Socket,0)==NULL)
00319 {
00320 LOG_WARN << "StDbServiceBroker::RecommendHost() mysql_real_connect "<<
00321 conn << " "<<((*I).HostName).c_str()<<" "<<(*I).Port <<" did not succeed"<<endm;
00322 mysql_close(conn);
00323 continue;
00324 }
00325
00326 if (mysql_query(conn, "show status like \"%Threads_running\"") != 0 )
00327 {
00328 LOG_WARN <<"StDbServiceBroker::RecommendHost() show processlist failed"<<endm;
00329 continue;
00330 }
00331
00332 unsigned int nproc = 0;
00333
00334
00335 MYSQL_RES* res_set = mysql_store_result(conn);
00336 if (res_set==0)
00337 {
00338 LOG_WARN << "StDbServiceBroker::RecommendHost(): mysql_store_result failed"<<endm;
00339 }
00340 else
00341 {
00342 MYSQL_ROW row = mysql_fetch_row(res_set);
00343 if (row==0)
00344 {
00345 LOG_WARN << "StDbServiceBroker::RecommendHost(): mysql_fetch_row failed"<<endm;
00346 }
00347 else
00348 {
00349 if(!from_string<unsigned int>(nproc,row[1],std::dec))
00350 {
00351 LOG_WARN << "StDbServiceBroker::RecommendHost(): mysql_fetch_row returns non-numeric"<<endm;
00352 }
00353 }
00354 mysql_free_result(res_set);
00355 }
00356
00357
00358
00359
00360
00361
00362 double dproc = nproc/(*I).Power;
00363 #ifdef DEBUG
00364 LOG_DEBUG <<" Server "<<((*I).HostName).c_str()<< " actual "<< nproc << " effective "<< dproc <<" processes "<<endm;
00365 #endif
00366 mysql_close(conn);
00367
00368 if (dproc<dproc_min && nproc<(*I).Cap)
00369 {
00370 dproc_min = dproc;
00371 MyBestHost = I;
00372 }
00373
00374 }
00375
00376 if ( MyBestHost != MyHostList.end() ) {
00377
00378 return 0;
00379 }
00380
00381 return 1;
00382 }
00384 string StDbServiceBroker::GiveHostName()
00385 {
00386 return (*MyBestHost).HostName;
00387 }
00389 short StDbServiceBroker::GiveHostPort()
00390 {
00391 return (*MyBestHost).Port;
00392 }
00394
00395 int StDbServiceBroker::updateLocalLbPolicy()
00396 {
00397
00398
00399
00400 string writableDir;
00401 string dbLoadBalancerConfig;
00402
00403 const char* loConfig = getenv("DB_SERVER_LOCAL_CONFIG");
00404 dbLoadBalancerConfig = loConfig ? loConfig : "";
00405
00406 if (!loConfig)
00407 {
00408 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_LOCAL_CONFIG is undefined! "<<endm;
00409 return lb_error::NO_LPD_ENV_VAR;
00410 }
00411 else
00412 {
00413 string::size_type last_slash = dbLoadBalancerConfig.find_last_of("/");
00414 writableDir = dbLoadBalancerConfig.substr(0,last_slash+1);
00415
00416
00417 struct stat dir_status;
00418 if (stat(writableDir.c_str(),&dir_status)==0)
00419 {
00420 if (dir_status.st_mode & S_IWUSR)
00421 {
00422
00423 }
00424 else
00425 {
00426 LOG_ERROR << "StDbManagerImpl::lookUpServers() "<<writableDir<<" is not writable"<<endm;
00427 return lb_error::NO_WRITE_PERMISSION;
00428 }
00429 }
00430 else
00431 {
00432 LOG_ERROR << "StDbManagerImpl::lookUpServers() invalid dir "<<writableDir <<endm;
00433 return lb_error::NO_LPD_DIR;
00434 }
00435
00436 }
00437
00439
00440
00441 struct stat file_status;
00442 bool fetchWorldConfig = true;
00443
00444 if (stat(dbLoadBalancerConfig.c_str(), &file_status) == 0)
00445 {
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 StlXmlTree myLittleWorldCheck = StlXmlTree(dbLoadBalancerConfig.c_str());
00456 #ifdef DEBUG
00457 LOG_INFO << "myLittleWorldCheck is:"<<endm;
00458 myLittleWorldCheck.ShowTree();
00459 #endif
00460 if (myLittleWorldCheck.GetStatus()==stl_xml_tree::NO_ERROR)
00461 {
00462 string key = StlXmlTree::MakeKey("","Scatalog");
00463
00464 vector<string> versions = myLittleWorldCheck.LookUpValueByKey(key);
00465
00466 string my_version = versions[0];
00467
00468 vector<string> sites = myLittleWorldCheck.LookUpValueByKey(key,my_version,"Site");
00469
00470 vector<string>::const_iterator I = sites.begin();
00471 bool WorldNotFound = true;
00472 if (sites.size()==0)
00473 {
00474
00475 WorldNotFound = false;
00476 }
00477 while( I!=sites.end() && WorldNotFound)
00478 {
00479 if (StlXmlTree::AttributesContain(*I,"name","World"))
00480 {
00481 WorldNotFound = false;
00482 }
00483 ++I;
00484 }
00485
00486 if (WorldNotFound)
00487 {
00488 #ifdef DEBUG
00489 LOG_INFO <<"StDbManagerImpl::updateLocalLbPolicy() protection against WWW XML is activated"<<endm;
00490 #endif
00491 fetchWorldConfig = false;
00492 }
00493 else
00494 {
00495 #ifdef DEBUG
00496 LOG_INFO<<"StDbManagerImpl::updateLocalLbPolicy() we found World, the user wants to read from the Web"<<endm;
00497 #endif
00498 }
00499
00500 if (fetchWorldConfig)
00501 {
00502 time_t glbModTime = file_status.st_mtime;
00503 time_t nowTime = time(0);
00504 if (nowTime-glbModTime<60)
00505 {
00506 fetchWorldConfig = false;
00507 }
00508 }
00509 }
00510 else
00511 {
00512 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy() invalid XML file "<<dbLoadBalancerConfig <<endm;
00513 }
00514 }
00515 else
00516 {
00517 LOG_ERROR << "StDbManagerImpl::lookUpServers(): config file " << dbLoadBalancerConfig << " is invalid "<< endm;
00518 }
00519
00520 if (fetchWorldConfig)
00521 {
00522
00523 const char* glConfig = getenv("DB_SERVER_GLOBAL_CONFIG");
00524 const string dbLoadBalancerWorldAFS = glConfig ? glConfig : "";
00525
00526 if (!glConfig)
00527 {
00528 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG is undefined! "<<endm;
00529 }
00530 else
00531 {
00532
00533 struct stat file_status;
00534 if (stat(dbLoadBalancerWorldAFS.c_str(), &file_status) == 0)
00535 {
00536 system(("/bin/cp "+dbLoadBalancerConfig+" "+dbLoadBalancerConfig+".old").c_str());
00537 system(("/bin/cp "+dbLoadBalancerWorldAFS+" "+dbLoadBalancerConfig).c_str());
00538 system(("/bin/chmod u+w "+dbLoadBalancerConfig).c_str());
00539 return lb_error::NO_ERROR;
00540 }
00541 else
00542 {
00543 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG points to invalid file! "<<endm;
00544 }
00545
00546 }
00547
00548
00549 const char* wwwConfig = getenv("DB_SERVER_GLOBAL_CONFIG_URL");
00550 const string dbLoadBalancerWorldURL = wwwConfig ? wwwConfig : "";
00551
00552 if (!wwwConfig)
00553 {
00554 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG_URL is undefined! "<<endm;
00555 return lb_error::NO_GPD_ENV_VAR;
00556 }
00557
00558 #ifdef DEBUG
00559 LOG_INFO <<"StDbManagerImpl::updateLocalLbPolicy() fetching world config "<<endm;
00560 #endif
00561 system(("cp "+dbLoadBalancerConfig+" "+dbLoadBalancerConfig+".old").c_str());
00562 const char* proxy = getenv("http_proxy");
00563 if (proxy)
00564 {
00565 xmlNanoHTTPScanProxy(proxy);
00566 }
00567
00568 int ret = xmlNanoHTTPFetch(dbLoadBalancerWorldURL.c_str(), dbLoadBalancerConfig.c_str(), 0);
00569
00570 if (ret!=0)
00571 {
00572 LOG_ERROR << "StDbManagerImpl::updateLocalLbPolicy() xmlNanoHTTPFetch error "<<ret<<endm;
00573 return lb_error::WWW_ERROR;
00574 }
00575 else
00576 {
00577 system(("chmod u+w "+dbLoadBalancerConfig).c_str());
00578 return lb_error::NO_ERROR;
00579 }
00580 }
00581 return lb_error::NO_ERROR;
00582
00583 }
00584
00585
00586 #endif