1 #ifndef NoXmlTreeReader
2 #include "StDbServiceBroker.h"
3 #include "ChapiStringUtilities.h"
7 #include <libxml/nanohttp.h>
11 #ifndef __STDB_STANDALONE__
12 #include "StMessMgr.h"
14 #define LOG_DEBUG cout
17 #define LOG_ERROR cerr
18 #define LOG_FATAL cerr
24 using namespace chapi_string_utilities;
26 typedef vector<string>::const_iterator VCI;
28 using st_db_service_broker::MyScatalogVersion;
29 using st_db_service_broker::NO_ERROR;
30 using st_db_service_broker::NO_USER;
31 using st_db_service_broker::NO_DOMAIN;
32 using st_db_service_broker::NO_HOSTS;
33 using st_db_service_broker::NightBegins;
34 using st_db_service_broker::NightEnds;
36 using stl_xml_tree::sep;
59 string SCOPE =
"scope";
60 string WHEN_ACTIVE =
"whenActive";
61 string ACCESS_MODE =
"accessMode";
62 string WRITER =
"writer";
63 string POWER =
"machinePower";
65 string INTERACTIVE =
"interactive";
74 StDbServiceBroker::StDbServiceBroker(
const string xmlbase) :
76 MyStatus(st_db_service_broker::NO_ERROR)
78 last_succeeded_connect_time = time(NULL);
79 seconds_to_reach_for_connect = 1800;
81 secs = getenv(
"STAR_DEBUG_DB_RETRIES_SECONDS");
83 seconds_to_reach_for_connect = atoi(secs);
86 char* whoami = getenv(
"USER");
87 if (!whoami) whoami = getenv(
"LOGNAME");
94 const char* access_mode = getenv(
"DB_ACCESS_MODE");
101 string ScatalogKey = sep+nn[SCATALOG];
102 f->InsertKeyValuePair(ScatalogKey, MyScatalogVersion);
104 string QualifiedScatalog = sep + StlXmlTree::QualifyParent(nn[SCATALOG],MyScatalogVersion);
105 string ServerKey = QualifiedScatalog + sep + nn[SERVER];
109 timeNow = time(NULL);
110 tp = localtime(&timeNow);
111 short hour = tp->tm_hour;
113 if (hour<NightBegins && hour >NightEnds)
119 DayOrNight =
"night";
123 std::string isInteractive =
"no";
124 char* testInteractive = 0;
125 testInteractive = getenv (
"INTERACTIVE");
127 if (testInteractive) {
128 isInteractive = testInteractive;
129 if (isInteractive ==
"1" || isInteractive ==
"YES" || isInteractive ==
"yes"
130 || isInteractive ==
"Yes" || isInteractive ==
"true" || isInteractive ==
"TRUE") {
131 isInteractive =
"yes";
133 isInteractive =
"no";
137 string ServerAttr = USER+
"="+(string)whoami+
";"+WHEN_ACTIVE+
"="+DayOrNight+
";"+ACCESS_MODE+
"="+access_mode+
";"+INTERACTIVE+
"="+isInteractive;
138 if (strcmp(whoami,
"starreco")==0)
140 ServerAttr = SCOPE+
"=Production;"+ServerAttr;
144 ServerAttr = SCOPE+
"=Analysis;"+ServerAttr;
146 f->InsertKeyValuePair(ServerKey, ServerAttr);
149 LOG_INFO <<
" Filter XML "<<endm;
154 if (ParsedXml.GetStatus()==stl_xml_tree::NO_XML_BASE)
156 MyStatus = st_db_service_broker::NO_XML_BASE;
157 LOG_WARN <<
"StDbServiceBroker::StDbServiceBroker: no XML description of services found "<<endm;
159 if (ParsedXml.GetStatus()==stl_xml_tree::BAD_XML)
161 MyStatus = st_db_service_broker::BAD_XML;
162 LOG_WARN <<
"StDbServiceBroker::StDbServiceBroker: malformed/incorrect XML description of services found "<<endm;
166 LOG_INFO <<
" Parsed XML "<<endm;
167 ParsedXml.ShowTree();
175 StDbServiceBroker::StDbServiceBroker
176 (
const string xmlbase,
const string xmlfilter) :
178 MyStatus(st_db_service_broker::NO_ERROR)
180 last_succeeded_connect_time = time(NULL);
181 seconds_to_reach_for_connect = 1800;
183 secs = getenv(
"STAR_DEBUG_DB_RETRIES_SECONDS");
185 seconds_to_reach_for_connect = atoi(secs);
193 if (ParsedXml.GetStatus()==stl_xml_tree::NO_XML_BASE)
195 MyStatus = st_db_service_broker::NO_XML_BASE;
196 LOG_WARN <<
"StDbServiceBroker::StDbServiceBroker: no XML description of services found "<<endm;
198 if (ParsedXml.GetStatus()==stl_xml_tree::BAD_XML)
200 MyStatus = st_db_service_broker::BAD_XML;
201 LOG_WARN <<
"StDbServiceBroker::StDbServiceBroker: malformed/incorrect XML description of services found "<<endm;
204 ParsedXml.ShowTree();
210 void StDbServiceBroker::DoLoadBalancing()
212 if (MyStatus!=st_db_service_broker::NO_ERROR)
214 LOG_ERROR <<
" StDbServiceBroker::DoLoadBalancing() errors" <<MyStatus<<endm;
219 bool host_found =
false;
220 const int MAX_COUNT = 500;
221 last_succeeded_connect_time = time(NULL);
222 for (
int i = 0; i < MAX_COUNT; i++) {
223 if (!RecommendHost()) {
227 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;
231 LOG_WARN <<
"Tried to find a host for " << MAX_COUNT <<
" times, will abort now." << endm;
236 cout <<
" go to "<<GiveHostName()<<
" port "<<GiveHostPort()<<
"\n";
240 void StDbServiceBroker::FormHostList()
250 string key = StlXmlTree::MakeKey(
"",nn[SCATALOG]);
251 vector<string> versions = ParsedXml.LookUpValueByKey(key);
253 if (versions.size()!=1)
return;
255 string my_version = versions[0];
257 vector<string> services = ParsedXml.LookUpValueByKey(key,my_version,nn[SERVER]);
259 for (VCI ii = services.begin(); ii!=services.end(); ++ii)
261 vector<string> hosts = ParsedXml.LookUpValueByKey(key,*ii,nn[HOST]);
263 for (VCI iii = hosts.begin(); iii!=hosts.end(); ++iii)
265 map<string,string> host_data = StlXmlTree::ParseAttributeString(*iii);
268 if (!from_string<int>(port,host_data[PORT],std::dec))
277 if (!from_string<double>(power,host_data[POWER],std::dec))
281 LOG_ERROR <<
"StDbServiceBroker::FormHostList(): non-numeric port, using default power for host "<<*iii<<endm;
283 power = DefaultPower;
287 if (!from_string<short>(cap,host_data[CAP],std::dec))
291 LOG_ERROR <<
"StDbServiceBroker::FormHostList(): non-numeric cap, using default cap for host "<<*iii<<endm;
297 MyHostList.push_back(host_entry);
299 cut_string_after_sub(key,
">");
300 cut_string_after_sub(key,
"(");
307 if (MyHostList.size()==0)
310 LOG_DEBUG<<
" StDbServiceBroker::RecommendHost() will have no hosts to choose from !"<<endm;
316 void StDbServiceBroker::PrintHostList()
318 LOG_DEBUG <<
" MyHostList contains:"<<endm;
319 for (vector<ChapiDbHost>::const_iterator i = MyHostList.begin(); i!=MyHostList.end(); ++i)
321 LOG_DEBUG << (*i).HostName << endm;
327 const std::string currentDateTime() {
328 time_t now = time(0);
331 tstruct = *localtime(&now);
332 strftime(buf,
sizeof(buf),
"%Y-%m-%d.%X", &tstruct);
338 void StDbServiceBroker::SendEmail(time_t timediff) {
340 std::string admin_emails;
341 char* admins = getenv(
"STAR_DEBUG_DB_RETRIES_ADMINS");
342 if (!admins) {
return; }
343 char* hostname = getenv(
"HOSTNAME");
344 pid_t mypid = getpid();
345 std::string host =
"unknown host";
346 if (hostname) { host = hostname; }
347 admin_emails = admins;
351 int res = stat(
"/tmp/db_network_error.txt", &attrib );
354 std::ofstream marker_file(
"/tmp/db_network_error.txt" );
355 if ( marker_file.is_open() ) {
356 marker_file << time(0);
358 chmod(
"/tmp/db_network_error.txt", 0666 );
361 if ( res == 0 && ( ( time(0) - attrib.st_mtime ) < seconds_to_reach_for_connect ) ) {
366 std::string curtime = currentDateTime();
367 std::stringstream exec_command;
368 exec_command <<
"echo \"We waited for " << timediff <<
" seconds (threshold: "<< seconds_to_reach_for_connect <<
"), and did not get a db connection at " << host
369 <<
" at " << curtime <<
", process id = " << mypid <<
"\" | /bin/mail -s \"DB RETRIES - Problem detected on "<< host <<
" at " << curtime <<
"\" " << admin_emails;
370 system(exec_command.str().c_str());
375 int StDbServiceBroker::RecommendHost()
377 double dproc_min = HUGE_VAL;
380 MyBestHost = MyHostList.end();
382 if (MyHostList.size()==1)
384 MyBestHost = MyHostList.begin();
388 srand (
unsigned ( time (NULL) ) );
389 random_shuffle( MyHostList.begin(), MyHostList.end() );
391 int scanned_hosts = 0;
392 bool host_found =
false;
393 for (vector<ChapiDbHost>::const_iterator I=MyHostList.begin(); I!=MyHostList.end(); ++I)
395 conn = mysql_init(0);
399 LOG_WARN <<
"StDbServiceBroker::RecommendHost() mysql_init(0) failed "<<endm;
403 conn->options.connect_timeout = 30;
405 if (mysql_real_connect
406 (conn,((*I).HostName).c_str(),
"loadbalancer",
"lbdb",
"test",(*I).Port,Socket,0)==NULL)
408 LOG_WARN <<
"StDbServiceBroker::RecommendHost() mysql_real_connect "<<
409 conn <<
" "<<((*I).HostName).c_str()<<
" "<<(*I).Port <<
" did not succeed"<<endm;
411 time_t timediff = time(NULL) - last_succeeded_connect_time;
412 if ( timediff > seconds_to_reach_for_connect) {
413 last_succeeded_connect_time = time(NULL);
418 last_succeeded_connect_time = time(NULL);
421 if (mysql_query(conn,
"show status like \"%Threads_running\"") != 0 )
423 LOG_WARN <<
"StDbServiceBroker::RecommendHost() show processlist failed"<<endm;
427 unsigned int nproc = 0;
430 MYSQL_RES* res_set = mysql_store_result(conn);
433 LOG_WARN <<
"StDbServiceBroker::RecommendHost(): mysql_store_result failed"<<endm;
437 MYSQL_ROW row = mysql_fetch_row(res_set);
440 LOG_WARN <<
"StDbServiceBroker::RecommendHost(): mysql_fetch_row failed"<<endm;
444 if(!from_string<unsigned int>(nproc,row[1],std::dec))
446 LOG_WARN <<
"StDbServiceBroker::RecommendHost(): mysql_fetch_row returns non-numeric"<<endm;
449 mysql_free_result(res_set);
457 double dproc = nproc/(*I).Power;
459 LOG_DEBUG <<
" Server "<<((*I).HostName).c_str()<<
" actual "<< nproc <<
" effective "<< dproc <<
" processes "<<endm;
464 if ( dproc<dproc_min && nproc<(*I).Cap ) {
471 if (scanned_hosts > 2 && host_found ==
true ) {
472 LOG_INFO <<
"StDbLib: db server found in " << scanned_hosts <<
" iterations" << endm;
477 if ( MyBestHost != MyHostList.end() ) {
485 string StDbServiceBroker::GiveHostName()
487 return (*MyBestHost).HostName;
490 short StDbServiceBroker::GiveHostPort()
492 return (*MyBestHost).Port;
496 int StDbServiceBroker::updateLocalLbPolicy()
502 string dbLoadBalancerConfig;
504 const char* loConfig = getenv(
"DB_SERVER_LOCAL_CONFIG");
505 dbLoadBalancerConfig = loConfig ? loConfig :
"";
509 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_LOCAL_CONFIG is undefined! "<<endm;
510 return lb_error::NO_LPD_ENV_VAR;
514 string::size_type last_slash = dbLoadBalancerConfig.find_last_of(
"/");
515 writableDir = dbLoadBalancerConfig.substr(0,last_slash+1);
518 struct stat dir_status;
519 if (stat(writableDir.c_str(),&dir_status)==0)
521 if (dir_status.st_mode & S_IWUSR)
527 LOG_ERROR <<
"StDbManagerImpl::lookUpServers() "<<writableDir<<
" is not writable"<<endm;
528 return lb_error::NO_WRITE_PERMISSION;
533 LOG_ERROR <<
"StDbManagerImpl::lookUpServers() invalid dir "<<writableDir <<endm;
534 return lb_error::NO_LPD_DIR;
542 struct stat file_status;
543 bool fetchWorldConfig =
true;
545 if (stat(dbLoadBalancerConfig.c_str(), &file_status) == 0)
558 LOG_INFO <<
"myLittleWorldCheck is:"<<endm;
559 myLittleWorldCheck.ShowTree();
561 if (myLittleWorldCheck.GetStatus()==stl_xml_tree::NO_ERROR)
563 string key = StlXmlTree::MakeKey(
"",
"Scatalog");
565 vector<string> versions = myLittleWorldCheck.LookUpValueByKey(key);
567 string my_version = versions[0];
569 vector<string> sites = myLittleWorldCheck.LookUpValueByKey(key,my_version,
"Site");
571 vector<string>::const_iterator I = sites.begin();
572 bool WorldNotFound =
true;
576 WorldNotFound =
false;
578 while( I!=sites.end() && WorldNotFound)
580 if (StlXmlTree::AttributesContain(*I,
"name",
"World"))
582 WorldNotFound =
false;
590 LOG_INFO <<
"StDbManagerImpl::updateLocalLbPolicy() protection against WWW XML is activated"<<endm;
592 fetchWorldConfig =
false;
597 LOG_INFO<<
"StDbManagerImpl::updateLocalLbPolicy() we found World, the user wants to read from the Web"<<endm;
601 if (fetchWorldConfig)
603 time_t glbModTime = file_status.st_mtime;
604 time_t nowTime = time(0);
605 if (nowTime-glbModTime<60)
607 fetchWorldConfig =
false;
613 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy() invalid XML file "<<dbLoadBalancerConfig <<endm;
618 LOG_ERROR <<
"StDbManagerImpl::lookUpServers(): config file " << dbLoadBalancerConfig <<
" is invalid "<< endm;
621 if (fetchWorldConfig)
624 const char* glConfig = getenv(
"DB_SERVER_GLOBAL_CONFIG");
625 const string dbLoadBalancerWorldAFS = glConfig ? glConfig :
"";
629 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG is undefined! "<<endm;
634 struct stat file_status;
635 if (stat(dbLoadBalancerWorldAFS.c_str(), &file_status) == 0)
637 system((
"/bin/cp "+dbLoadBalancerConfig+
" "+dbLoadBalancerConfig+
".old").c_str());
638 system((
"/bin/cp "+dbLoadBalancerWorldAFS+
" "+dbLoadBalancerConfig).c_str());
639 system((
"/bin/chmod u+w "+dbLoadBalancerConfig).c_str());
640 return lb_error::NO_ERROR;
644 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG points to invalid file! "<<endm;
650 const char* wwwConfig = getenv(
"DB_SERVER_GLOBAL_CONFIG_URL");
651 const string dbLoadBalancerWorldURL = wwwConfig ? wwwConfig :
"";
655 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy(): DB_SERVER_GLOBAL_CONFIG_URL is undefined! "<<endm;
656 return lb_error::NO_GPD_ENV_VAR;
660 LOG_INFO <<
"StDbManagerImpl::updateLocalLbPolicy() fetching world config "<<endm;
662 system((
"cp "+dbLoadBalancerConfig+
" "+dbLoadBalancerConfig+
".old").c_str());
663 const char* proxy = getenv(
"http_proxy");
666 xmlNanoHTTPScanProxy(proxy);
669 int ret = xmlNanoHTTPFetch(dbLoadBalancerWorldURL.c_str(), dbLoadBalancerConfig.c_str(), 0);
673 LOG_ERROR <<
"StDbManagerImpl::updateLocalLbPolicy() xmlNanoHTTPFetch error "<<ret<<endm;
674 return lb_error::WWW_ERROR;
678 system((
"chmod u+w "+dbLoadBalancerConfig).c_str());
679 return lb_error::NO_ERROR;
682 return lb_error::NO_ERROR;