ws_machineServiceMetrics.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #pragma warning (disable : 4786)
  14. #include <map>
  15. #include <vector>
  16. #include <math.h>
  17. #include "ws_machineService.hpp"
  18. #include "jarray.hpp"
  19. #include "jmisc.hpp"
  20. #include "thirdparty.h"
  21. #include "portlist.h"
  22. #include "roxiecommlib.hpp"
  23. #include "exception_util.hpp"
  24. //---------------------------------------------------------------------------------------------
  25. //NOTE: PART II of implementation for Cws_machineEx
  26. // PART I and III are in ws_machineService.cpp and ws_machineServiceRexec.cpp resp.
  27. //---------------------------------------------------------------------------------------------
  28. static const char* METRICS_FEATURE_URL = "MetricsAccess";
  29. static const char* OID = "1.3.6.1.4.1.12723.6.16.1.4.1.2";
  30. // We need to compute standard deviation for metrics information.
  31. // The classical algorithm to do so requires two passes of data -
  32. // first pass to compute mean and the second to compute deviations
  33. // as follows:
  34. //
  35. // SD = SQRT( SUM( (Xi-M)^2 ) / (n-1) )
  36. //
  37. // where Xi is X1, X2...Xn and M is their mean.
  38. //
  39. // To compute SD in a single pass of data, we use the following algorithm:
  40. // We maintain 3 variables (for each field) n (count), M (mean), and SSD (Sum of
  41. // squared deviations i.e. SUM((Xi-Mean)^2 ).
  42. //
  43. // Begin by setting N=1, M=X1 (first sample), and SSD=0.
  44. // For every subsequent sample X, we update these values incrementallyas follows:
  45. //
  46. // N++
  47. // compute deviation, D:=(X-M)/N. This is how much the mean will change with new data.
  48. // M += D
  49. // SSD += (N-1) * D^2 + (X-M)^2.
  50. //
  51. // We now have the updated mean and sum of squared deviations, SSD.
  52. // After all of the data is digested in this way, just calculate variance:=SSD/(n-1)
  53. // and standard_deviation := sqrt(variance).
  54. // It requires no memory to hold all of the data, only one pass through the data, formula,
  55. // and it gives the correct results for a much wider range of data.
  56. //
  57. #ifdef OLD
  58. struct CField
  59. {
  60. float Value;
  61. bool Warn;
  62. bool Undefined;
  63. CField()
  64. : Value(0), Warn(0), Undefined(0)
  65. {
  66. }
  67. void serialize(StringBuffer& xml) const
  68. {
  69. xml.append("<Field>");
  70. xml.appendf("<Value>%f</Value>", Value);
  71. if (Warn)
  72. xml.append("<Warn>1</Warn>");
  73. if (Undefined)
  74. xml.append("<Undefined>1</Undefined>");
  75. xml.append("</Field>");
  76. }
  77. };
  78. struct CFieldMap : public map<string, CField*>
  79. {
  80. virtual ~CFieldMap()
  81. {
  82. const_iterator iEnd=end();
  83. for (const_iterator i=begin(); i!=iEnd; i++)
  84. delete (*i).second;
  85. }
  86. void serialize(StringBuffer& xml)
  87. {
  88. xml.append("<Fields>");
  89. const_iterator iEnd=end();
  90. for (const_iterator i=begin(); i!=iEnd; i++)
  91. (*i).second->serialize(xml);
  92. xml.append("</Fields>");
  93. }
  94. };
  95. struct CFieldInfo
  96. {
  97. unsigned Count; //N
  98. float SumSquaredDeviations; //SSD
  99. float Mean;
  100. float StandardDeviation;
  101. bool Hide;
  102. CFieldInfo()
  103. : Count(0),
  104. SumSquaredDeviations(0),
  105. Mean(0),
  106. StandardDeviation(0),
  107. Hide(true)
  108. {
  109. }
  110. void serialize(StringBuffer& xml, const char* fieldName) const
  111. {
  112. xml.append("<FieldInfo>");
  113. xml.appendf("<Name>%s</Name>", fieldName);
  114. xml.append("<Caption>");
  115. const char* pch = fieldName;
  116. if (!strncmp(pch, "lo", 2))
  117. {
  118. xml.append("Low");
  119. pch += 2;
  120. }
  121. else if (!strncmp(pch, "hi", 2))
  122. {
  123. xml.append("High");
  124. pch += 2;
  125. }
  126. else if (!strncmp(pch, "tot", 3))
  127. {
  128. xml.append("Total");
  129. pch += 3;
  130. }
  131. else xml.append( (char)toupper( *pch++) );
  132. while (*pch)
  133. {
  134. if (isupper(*pch))
  135. xml.append(' ');
  136. xml.append(*pch++);
  137. }
  138. xml.append("</Caption>");
  139. xml.appendf("<Mean>%f</Mean>", Mean);
  140. xml.appendf("<StandardDeviation>%f</StandardDeviation>", StandardDeviation);
  141. if (Hide)
  142. xml.appendf("<Hide>1</Hide>");
  143. xml.append("</FieldInfo>");
  144. }
  145. };
  146. struct CFieldInfoMap : public map<string, CFieldInfo*>
  147. {
  148. Mutex m_mutex;
  149. virtual ~CFieldInfoMap()
  150. {
  151. const_iterator iEnd=end();
  152. for (const_iterator i=begin(); i!=iEnd; i++)
  153. delete (*i).second;
  154. }
  155. void serialize(StringBuffer& xml) const
  156. {
  157. const_iterator iEnd=end();
  158. for (const_iterator i=begin(); i!=iEnd; i++)
  159. {
  160. const char* fieldName = (*i).first.c_str();
  161. (*i).second->serialize(xml, fieldName);
  162. }
  163. }
  164. };
  165. //---------------------------------------------------------------------------------------------
  166. class CSnmpWalkerCallback : public CInterface,
  167. implements ISnmpWalkerCallback
  168. {
  169. private:
  170. public:
  171. IMPLEMENT_IINTERFACE;
  172. CSnmpWalkerCallback(CFieldInfoMap& fieldInfoMap, CFieldMap& fieldMap)
  173. : m_fieldInfoMap(fieldInfoMap), m_fieldMap(fieldMap)
  174. {}
  175. virtual ~CSnmpWalkerCallback(){}
  176. virtual void processValue(const char *oid, const char *value)
  177. {
  178. double val = atof(value);
  179. CField* pField = new CField;
  180. pField->Value = val;
  181. if (!strncmp(oid, "ibyti", 5))
  182. oid += 5;
  183. m_fieldMap.insert(pair<const char*, CField*>( oid, pField) );
  184. synchronized block(m_fieldInfoMap.m_mutex);
  185. CFieldInfoMap::iterator i = m_fieldInfoMap.find(oid);
  186. if (i == m_fieldInfoMap.end())
  187. {
  188. CFieldInfo* pFieldInfo = new CFieldInfo;
  189. pFieldInfo->Count = 1;
  190. pFieldInfo->Mean = val;
  191. pFieldInfo->SumSquaredDeviations = 0;
  192. m_fieldInfoMap.insert( pair<const char*, CFieldInfo*>(oid, pFieldInfo) );
  193. }
  194. else
  195. {
  196. CFieldInfo* pFieldInfo = (*i).second;
  197. pFieldInfo->Count++;
  198. double deviation = (val - pFieldInfo->Mean) / pFieldInfo->Count;
  199. pFieldInfo->Mean = pFieldInfo->Mean + deviation;
  200. pFieldInfo->SumSquaredDeviations += (pFieldInfo->Count-1) * (deviation * deviation);
  201. double temp = val - pFieldInfo->Mean;
  202. pFieldInfo->SumSquaredDeviations += temp * temp;
  203. }
  204. }
  205. private:
  206. CSnmpWalkerCallback(const CSnmpWalkerCallback&);
  207. CFieldInfoMap& m_fieldInfoMap;
  208. CFieldMap& m_fieldMap;
  209. };
  210. //---------------------------------------------------------------------------------------------
  211. class CMetricsThreadParam : public CWsMachineThreadParam
  212. {
  213. public:
  214. IMPLEMENT_IINTERFACE;
  215. CFieldInfoMap& m_fieldInfoMap;
  216. CFieldMap m_fieldMap;
  217. PooledThreadHandle m_threadHandle;
  218. bool m_bPostProcessing; //use mean & std deviation to set warnings, etc.
  219. CMetricsThreadParam( const char* pszAddress, const char* pszSecString,
  220. CFieldInfoMap& fieldInfoMap,
  221. Cws_machineEx* pService)
  222. : CWsMachineThreadParam(pszAddress, pszSecString, pService),
  223. m_fieldInfoMap(fieldInfoMap),
  224. m_bPostProcessing(false)
  225. {
  226. }
  227. virtual ~CMetricsThreadParam()
  228. {
  229. }
  230. virtual void doWork()
  231. {
  232. if (m_bPostProcessing == false)
  233. m_pService->doGetMetrics(this);
  234. else
  235. doPostProcessing();
  236. }
  237. void doPostProcessing()
  238. {
  239. //DBGLOG("Post processing for %s", m_sAddress.str());
  240. //for each field in the field info map (some of these fields may not be defined
  241. //in our field map)
  242. //
  243. CFieldInfoMap::iterator i;
  244. CFieldInfoMap::iterator iEnd = m_fieldInfoMap.end();
  245. for (i=m_fieldInfoMap.begin(); i!=iEnd; i++)
  246. {
  247. const char *fieldName = (*i).first.c_str();
  248. const CFieldInfo* pFieldInfo = (*i).second;
  249. CFieldMap::iterator iField = m_fieldMap.find(fieldName);
  250. if (iField == m_fieldMap.end())
  251. {
  252. CField* pField = new CField;
  253. pField->Undefined = true;
  254. m_fieldMap.insert(pair<const char*, CField*>( fieldName, pField ));
  255. }
  256. else
  257. {
  258. CField* pField = (*iField).second;
  259. //set warnings based on mean and standard deviation
  260. double z = fabs((pField->Value - pFieldInfo->Mean) / pFieldInfo->StandardDeviation);
  261. if (z > 2)
  262. pField->Warn = true;
  263. }
  264. }
  265. }
  266. };
  267. void Cws_machineEx::doGetMetrics(CMetricsThreadParam* pParam)
  268. {
  269. //DBGLOG("Getting Metrics for %s", pParam->m_sAddress.str());
  270. StringBuffer sSecurityString = pParam->m_sSecurityString;
  271. if (!*sSecurityString.str())
  272. doGetSecurityString(pParam->m_sAddress.str(), sSecurityString);
  273. #ifdef _DEBUG
  274. if (!*sSecurityString.str())
  275. sSecurityString.append("M0n1T0r");
  276. #endif
  277. //serialized for now
  278. }
  279. #else
  280. //-------------------------------------------------METRICS------------------------------------------------------
  281. void Cws_machineEx::getRoxieClusterConfig(char const * clusterType, char const * clusterName, char const * processName, StringBuffer& netAddress, int& port)
  282. {
  283. #if 0
  284. Owned<IEnvironmentFactory> factory = getEnvironmentFactory();
  285. Owned<IConstEnvironment> environment = factory->openEnvironment();
  286. Owned<IPropertyTree> pRoot = &environment->getPTree();
  287. #else
  288. Owned<IConstEnvironment> constEnv = getConstEnvironment();
  289. Owned<IPropertyTree> pRoot = &constEnv->getPTree();
  290. if (!pRoot)
  291. throw MakeStringException(ECLWATCH_CANNOT_GET_ENV_INFO,"Failed to get environment information.");
  292. #endif
  293. StringBuffer xpath;
  294. xpath.appendf("Software/%s[@name='%s']", clusterType, clusterName);
  295. IPropertyTree* pCluster = pRoot->queryPropTree( xpath.str() );
  296. if (!pCluster)
  297. throw MakeStringException(ECLWATCH_CLUSTER_NOT_IN_ENV_INFO, "'%s %s' is not defined!", clusterType, clusterName);
  298. xpath.clear().append(processName);
  299. xpath.append("@computer");
  300. const char* computer = pCluster->queryProp(xpath.str());
  301. if (!computer || strlen(computer) < 1)
  302. throw MakeStringException(ECLWATCH_INVALID_CLUSTER_INFO, "'%s %s: %s' is not defined!", clusterType, clusterName, processName);
  303. xpath.clear().append(processName);
  304. xpath.append("@port");
  305. const char* portStr = pCluster->queryProp(xpath.str());
  306. port = ROXIE_SERVER_PORT;
  307. if (portStr && *portStr)
  308. {
  309. port = atoi(portStr);
  310. }
  311. #if 0
  312. Owned<IConstMachineInfo> pMachine = environment->getMachine(computer);
  313. if (pMachine)
  314. {
  315. SCMStringBuffer scmNetAddress;
  316. pMachine->getNetAddress(scmNetAddress);
  317. netAddress = scmNetAddress.str();
  318. }
  319. #else
  320. xpath.clear().appendf("Hardware/Computer[@name=\"%s\"]", computer);
  321. IPropertyTree* pMachine = pRoot->queryPropTree( xpath.str() );
  322. if (pMachine)
  323. {
  324. const char* addr = pMachine->queryProp("@netAddress");
  325. if (addr && *addr)
  326. netAddress.append(addr);
  327. }
  328. #endif
  329. return;
  330. }
  331. void Cws_machineEx::processValue(const char *oid, const char *value, const bool bShow, CFieldInfoMap& myfieldInfoMap, CFieldMap& myfieldMap)
  332. {
  333. double val = atof(value);
  334. CField* pField = new CField;
  335. pField->Value = val;
  336. //const char *oid0 = oid;
  337. //if (!strncmp(oid, "ibyti", 5))
  338. // oid += 5;
  339. pField->Hide = !bShow;
  340. myfieldMap.insert(pair<const char*, CField*>( oid, pField) );
  341. synchronized block(myfieldInfoMap.m_mutex);
  342. CFieldInfoMap::iterator i = myfieldInfoMap.find(oid);
  343. if (i == myfieldInfoMap.end())
  344. {
  345. CFieldInfo* pFieldInfo = new CFieldInfo;
  346. pFieldInfo->Count = 1;
  347. pFieldInfo->Mean = val;
  348. pFieldInfo->SumSquaredDeviations = 0;
  349. pFieldInfo->Hide = !bShow;
  350. myfieldInfoMap.insert( pair<const char*, CFieldInfo*>(oid, pFieldInfo) );
  351. }
  352. else
  353. {
  354. CFieldInfo* pFieldInfo = (*i).second;
  355. pFieldInfo->Count++;
  356. double deviation = (val - pFieldInfo->Mean) / pFieldInfo->Count;
  357. pFieldInfo->Mean = pFieldInfo->Mean + deviation;
  358. pFieldInfo->Hide = !bShow;
  359. pFieldInfo->SumSquaredDeviations += (pFieldInfo->Count-1) * (deviation * deviation);
  360. double temp = val - pFieldInfo->Mean;
  361. pFieldInfo->SumSquaredDeviations += temp * temp;
  362. }
  363. }
  364. void Cws_machineEx::doPostProcessing(CFieldInfoMap& myfieldInfoMap, CFieldMap& myfieldMap)
  365. {
  366. //DBGLOG("Post processing for %s", m_sAddress.str());
  367. //for each field in the field info map (some of these fields may not be defined
  368. //in our field map)
  369. //
  370. CFieldInfoMap::iterator i;
  371. CFieldInfoMap::iterator iEnd = myfieldInfoMap.end();
  372. for (i=myfieldInfoMap.begin(); i!=iEnd; i++)
  373. {
  374. const char *fieldName = (*i).first.c_str();
  375. const CFieldInfo* pFieldInfo = (*i).second;
  376. CFieldMap::iterator iField = myfieldMap.find(fieldName);
  377. if (iField == myfieldMap.end())
  378. {
  379. CField* pField = new CField;
  380. pField->Undefined = true;
  381. myfieldMap.insert(pair<const char*, CField*>( fieldName, pField ));
  382. }
  383. else
  384. {
  385. CField* pField = (*iField).second;
  386. //set warnings based on mean and standard deviation
  387. double z = fabs((pField->Value - pFieldInfo->Mean) / pFieldInfo->StandardDeviation);
  388. if (z > 2)
  389. pField->Warn = true;
  390. }
  391. }
  392. }
  393. #endif
  394. bool Cws_machineEx::onGetMetrics(IEspContext &context, IEspMetricsRequest &req,
  395. IEspMetricsResponse &resp)
  396. {
  397. try
  398. {
  399. context.ensureFeatureAccess(METRICS_FEATURE_URL, SecAccess_Read, ECLWATCH_METRICS_ACCESS_DENIED, "Failed to Get Metrics. Permission denied.");
  400. //insert entries in an array - one per IP address, sorted by IP address
  401. //
  402. const char* clusterName = req.getCluster();
  403. if (!clusterName || !*clusterName)
  404. throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Cluster name not defined.");
  405. resp.setCluster(clusterName);
  406. StringArray &addresses = req.getAddresses();
  407. unsigned int ordinality= addresses.ordinality();
  408. unsigned int addressCount = 0;
  409. unsigned* buffer = NULL;
  410. unsigned index;
  411. for (index=0; index<ordinality; index++)
  412. {
  413. const char *address = addresses.item(index);
  414. const char *colon = strchr(address, ':');
  415. char* address2 = NULL;
  416. if (colon)
  417. {
  418. address2 = strdup(address);
  419. address2[colon-address] = '\0';
  420. address = address2;
  421. }
  422. char* configAddress = NULL;
  423. char* props1 = (char*) strchr(address, '|');
  424. if (props1)
  425. {
  426. configAddress = props1+1;
  427. *props1 = '\0';
  428. }
  429. else
  430. {
  431. configAddress = (char*) address;
  432. }
  433. addIpAddressesToBuffer( (void**)&buffer, addressCount, address);
  434. if (address2)
  435. free(address2);
  436. }
  437. CFieldInfoMap fieldInfoMap; //shared across all threads processing this request
  438. #ifdef OLD
  439. CIArrayOf<CMetricsThreadParam> threadParamArray;
  440. //process this array (sorted by IP address)
  441. //
  442. // TBD IPV6 (cannot use long for netaddress)
  443. StringBuffer ipBuf;
  444. unsigned* lptr = buffer;
  445. for (index=0; index<addressCount; index++)
  446. {
  447. IpAddress ip;
  448. ip.setNetAddress(sizeof(unsigned),lptr++);
  449. ip.getIpText(ipBuf.clear());
  450. CMetricsThreadParam* pThreadReq =
  451. new CMetricsThreadParam(ipBuf.str(), req.getSecurityString(),
  452. fieldInfoMap, this);
  453. threadParamArray.append(*::LINK(pThreadReq));
  454. pThreadReq->m_threadHandle = m_threadPool->start( pThreadReq );
  455. }
  456. if (buffer)
  457. ::free(buffer);
  458. //block for worker theads to finish, if necessary and then collect results
  459. //
  460. CMetricsThreadParam** pThreadParam = (CMetricsThreadParam**) threadParamArray.getArray();
  461. unsigned count=threadParamArray.ordinality();
  462. unsigned i;
  463. for (i = 0; i < count; i++, pThreadParam++)
  464. m_threadPool->join((*pThreadParam)->m_threadHandle);
  465. //collect field information for all fields
  466. CFieldInfoMap::iterator iInfo;
  467. CFieldInfoMap::iterator iInfoEnd = fieldInfoMap.end();
  468. for (iInfo=fieldInfoMap.begin(); iInfo!=iInfoEnd; iInfo++)
  469. {
  470. CFieldInfo* pFieldInfo = (*iInfo).second;
  471. pFieldInfo->StandardDeviation = ( pFieldInfo->Count > 1 ? sqrt(pFieldInfo->SumSquaredDeviations / (pFieldInfo->Count-1)) : 0);
  472. }
  473. //respect user's wishes to only show some columns
  474. //
  475. StringArray& showColumns = req.getShowColumns();
  476. unsigned int columnsToShow = showColumns.ordinality();
  477. if (columnsToShow == 0)
  478. {
  479. static const char* defaultColumns[] = {
  480. "heapBlocksAllocated", "hiQueryActive", "hiQueryAverage", "hiQueryCount", "hiMax", "hiMin",
  481. "lastQueryDate", "lastQueryTime", "loMax", "loMin", "loQueryActive", "loQueryAverage",
  482. "loQueryCount", "retriesNeeded", "slavesActive"
  483. };
  484. columnsToShow = sizeof(defaultColumns)/sizeof(defaultColumns[0]);
  485. for (unsigned int i=0; i<columnsToShow; i++)
  486. {
  487. iInfo = fieldInfoMap.find(defaultColumns[i]);
  488. if (iInfo != iInfoEnd)
  489. (*iInfo).second->Hide = 0;
  490. }
  491. }
  492. else
  493. for (index=0; index<columnsToShow; index++)
  494. {
  495. const char *columnName = showColumns.item(index);
  496. iInfo = fieldInfoMap.find(columnName);
  497. if (iInfo != iInfoEnd)
  498. (*iInfo).second->Hide = 0;
  499. }
  500. //create a separate thread to do post processing i.e. serialize field map
  501. //to field array while filling in any absent fields and set warnings for fields
  502. //with very high deviation
  503. //
  504. pThreadParam = (CMetricsThreadParam**) threadParamArray.getArray();
  505. for (i = 0; i < count; i++, pThreadParam++)
  506. {
  507. (*pThreadParam)->m_bPostProcessing = true;
  508. (*pThreadParam)->m_threadHandle = m_threadPool->start( ::LINK(*pThreadParam) );
  509. }
  510. StringBuffer xml;
  511. fieldInfoMap.serialize(xml);
  512. resp.setFieldInformation(xml);
  513. xml.clear();
  514. pThreadParam = (CMetricsThreadParam**) threadParamArray.getArray();
  515. for (i = 0; i < count; i++, pThreadParam++)
  516. {
  517. xml.append("<MetricsInfo><Address>");
  518. xml.append( (*pThreadParam)->m_sAddress );
  519. xml.append("</Address>");
  520. //block for worker theads to finish, if necessary, and then collect results
  521. //
  522. m_threadPool->join((*pThreadParam)->m_threadHandle);
  523. (*pThreadParam)->m_fieldMap.serialize(xml);
  524. xml.append("</MetricsInfo>");
  525. }
  526. resp.setMetrics(xml);
  527. #else
  528. //process this array (sorted by IP address)
  529. //
  530. // TBD IPV6 (cannot use long for netaddress)
  531. StringArray ipList;
  532. StringBuffer ipBuf;
  533. unsigned* lptr = buffer;
  534. for (index=0; index<addressCount; index++)
  535. {
  536. IpAddress ip;
  537. ip.setNetAddress(sizeof(unsigned),lptr++);
  538. ip.getIpText(ipBuf.clear());
  539. ipList.append(ipBuf.str());
  540. }
  541. if (buffer)
  542. ::free(buffer);
  543. int port;
  544. StringBuffer netAddress;
  545. SocketEndpoint ep;
  546. getRoxieClusterConfig("RoxieCluster", clusterName, "RoxieServerProcess[1]", netAddress, port);
  547. StringArray& showColumns = req.getShowColumns();
  548. unsigned int columnsToShow = showColumns.ordinality();
  549. ep.set(netAddress.str(), port);
  550. Owned<IRoxieCommunicationClient> roxieClient = createRoxieCommunicationClient(ep, 5000);
  551. Owned<IPropertyTree> result = roxieClient->retrieveRoxieMetrics(ipList);
  552. CIArrayOf<CMetricsParam> fieldMapArray;
  553. Owned<IPropertyTreeIterator> endpoints = result->getElements("Endpoint");
  554. ForEach(*endpoints)
  555. {
  556. IPropertyTree &endpoint = endpoints->query();
  557. CFieldMap fieldMap;
  558. const char* ep = endpoint.queryProp("@ep");
  559. if (!ep || !*ep)
  560. continue;
  561. StringBuffer ip(ep);
  562. const char* ip0 = strchr(ep, ':');
  563. if (ip0)
  564. ip.setLength(ip0 - ep);
  565. Owned<CMetricsParam> pMetricsParam = new CMetricsParam(ip);
  566. Owned<IPropertyTreeIterator> metrics = endpoint.getElements("Metrics/Metric");
  567. ForEach(*metrics)
  568. {
  569. IPropertyTree &metric = metrics->query();
  570. const char* name = metric.queryProp("@name");
  571. const char* value = metric.queryProp("@value");
  572. if (!name || !*name || !value || !*value)
  573. continue;
  574. //const char* name0 = name;
  575. //if (!strncmp(name0, "ibyti", 5))
  576. // name0 += 5;
  577. bool bShow = false;
  578. if (columnsToShow == 0)
  579. {
  580. static const char* defaultColumns[] = {
  581. "heapBlocksAllocated", "hiQueryActive", "hiQueryAverage", "hiQueryCount", "hiMax", "hiMin",
  582. "lastQueryDate", "lastQueryTime", "loMax", "loMin", "loQueryActive", "loQueryAverage",
  583. "loQueryCount", "retriesNeeded", "slavesActive"
  584. };
  585. unsigned int columnsToShow0 = sizeof(defaultColumns)/sizeof(defaultColumns[0]);
  586. for (unsigned int i=0; i<columnsToShow0; i++)
  587. {
  588. if (!stricmp(defaultColumns[i], name))
  589. {
  590. bShow = true;
  591. break;
  592. }
  593. }
  594. }
  595. else
  596. {
  597. for (index=0; index<columnsToShow; index++)
  598. {
  599. const char *columnName = showColumns.item(index);
  600. if (!stricmp(columnName, name))
  601. {
  602. bShow = true;
  603. break;
  604. }
  605. }
  606. }
  607. processValue(name, value, bShow, fieldInfoMap, pMetricsParam->m_fieldMap);
  608. }
  609. fieldMapArray.append(*pMetricsParam.getLink());
  610. }
  611. int count=fieldMapArray.ordinality();
  612. //collect field information for all fields
  613. CFieldInfoMap::iterator iInfo;
  614. CFieldInfoMap::iterator iInfoEnd = fieldInfoMap.end();
  615. for (iInfo=fieldInfoMap.begin(); iInfo!=iInfoEnd; iInfo++)
  616. {
  617. CFieldInfo* pFieldInfo = (*iInfo).second;
  618. pFieldInfo->StandardDeviation = ( pFieldInfo->Count > 1 ? sqrt(pFieldInfo->SumSquaredDeviations / (pFieldInfo->Count-1)) : 0);
  619. }
  620. //respect user's wishes to only show some columns
  621. //
  622. if (columnsToShow == 0)
  623. {
  624. static const char* defaultColumns[] = {
  625. "heapBlocksAllocated", "hiQueryActive", "hiQueryAverage", "hiQueryCount", "hiMax", "hiMin",
  626. "lastQueryDate", "lastQueryTime", "loMax", "loMin", "loQueryActive", "loQueryAverage",
  627. "loQueryCount", "retriesNeeded", "slavesActive"
  628. };
  629. columnsToShow = sizeof(defaultColumns)/sizeof(defaultColumns[0]);
  630. for (unsigned int i=0; i<columnsToShow; i++)
  631. {
  632. iInfo = fieldInfoMap.find(defaultColumns[i]);
  633. if (iInfo != iInfoEnd)
  634. (*iInfo).second->Hide = 0;
  635. }
  636. }
  637. else
  638. for (index=0; index<columnsToShow; index++)
  639. {
  640. const char *columnName = showColumns.item(index);
  641. iInfo = fieldInfoMap.find(columnName);
  642. if (iInfo != iInfoEnd)
  643. (*iInfo).second->Hide = 0;
  644. }
  645. //create a separate thread to do post processing i.e. serialize field map
  646. //to field array while filling in any absent fields and set warnings for fields
  647. //with very high deviation
  648. //
  649. int i = 0;
  650. CMetricsParam** pMetricsParam = (CMetricsParam**) fieldMapArray.getArray();
  651. for (i = 0; i < count; i++, pMetricsParam++)
  652. {
  653. doPostProcessing(fieldInfoMap, (*pMetricsParam)->m_fieldMap);
  654. }
  655. StringBuffer xml;
  656. fieldInfoMap.serialize(xml);
  657. resp.setFieldInformation(xml);
  658. xml.clear();
  659. pMetricsParam = (CMetricsParam**) fieldMapArray.getArray();
  660. for (i = 0; i < count; i++, pMetricsParam++)
  661. {
  662. xml.append("<MetricsInfo><Address>");
  663. xml.append( (*pMetricsParam)->m_sAddress);
  664. xml.append("</Address>");
  665. (*pMetricsParam)->m_fieldMap.serialize(xml);
  666. xml.append("</MetricsInfo>");
  667. }
  668. resp.setMetrics(xml);
  669. #endif
  670. double version = context.getClientVersion();
  671. if (version > 1.05)
  672. {
  673. resp.setSelectAllChecked( req.getSelectAllChecked() );
  674. }
  675. if (version > 1.06)
  676. {
  677. resp.setAutoUpdate( req.getAutoUpdate() );
  678. }
  679. if (version >= 1.12)
  680. {
  681. StringBuffer acceptLanguage;
  682. resp.setAcceptLanguage(getAcceptLanguage(context, acceptLanguage).str());
  683. }
  684. resp.setAutoRefresh( req.getAutoRefresh() );//loop back requested auto refresh timeout to output so javascript sets timeout
  685. }
  686. catch(IException* e)
  687. {
  688. FORWARDEXCEPTION(context, e, ECLWATCH_INTERNAL_ERROR);
  689. }
  690. return true;
  691. }
  692. // TBD IPv6
  693. static int compareNumericIp(const void* ip1, const void* ip2)
  694. {
  695. const unsigned numIP1 = *(unsigned*)ip1;
  696. const unsigned numIP2 = *(unsigned*)ip2;
  697. return numIP1 == numIP2 ? 0 : numIP1 < numIP2 ? -1 : 1;
  698. }
  699. void Cws_machineEx::addIpAddressesToBuffer( void** buffer, unsigned& count, const char* address)
  700. {
  701. IpAddress fromIp;
  702. unsigned numIPs = fromIp.ipsetrange(address);
  703. if (numIPs==0)
  704. throw MakeStringException(ECLWATCH_INVALID_IP_RANGE, "Invalid IP address range '%s'.", address);
  705. //resize the array
  706. *buffer = realloc(*buffer, sizeof(unsigned) * (count+numIPs));
  707. //insert first address in the array
  708. bool bAdded;
  709. // TBD IPv6
  710. if (!fromIp.isIp4())
  711. IPV6_NOT_IMPLEMENTED();
  712. unsigned ip;
  713. if (fromIp.getNetAddress(sizeof(ip),&ip)!=sizeof(ip))
  714. IPV6_NOT_IMPLEMENTED(); // Not quite same exception, but re-use when IPv4 hack fails sanity check
  715. unsigned* pos = (unsigned*) binary_add((void*)&ip, *buffer, count, sizeof(ip),
  716. compareNumericIp, &bAdded);
  717. //now insert all subsequent addresses, if any, in the address range as contiguous
  718. //memory assuming the ranges in the buffer don't overlap
  719. //
  720. if (bAdded)
  721. {
  722. count++;
  723. if (--numIPs > 0)
  724. {
  725. //at this point, one element has been inserted at position 'pos' in the buffer
  726. //so we need to make room for subsequent elements in the range by pushing the
  727. //existing elements behind the position 'pos' by (numIPs-1) places
  728. //
  729. pos++; //points to position after first inserted element
  730. unsigned index = pos - (unsigned*)*buffer;//index of next item
  731. unsigned itemsToMove = count - index;
  732. memmove(pos+numIPs, pos, itemsToMove*sizeof(ip));
  733. count += numIPs;
  734. while (numIPs--)
  735. {
  736. fromIp.ipincrement(1);
  737. if (fromIp.getNetAddress(sizeof(*pos),pos)!=sizeof(unsigned))
  738. IPV6_NOT_IMPLEMENTED();
  739. pos++;
  740. }
  741. }
  742. }
  743. }