TS-2977: move the stats processor to traffic_manager

Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/9e6d233f
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/9e6d233f
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/9e6d233f

Branch: refs/heads/master
Commit: 9e6d233fe647863adb7b093cd6643739f73c7c8e
Parents: 7386583
Author: James Peach <jpe...@apache.org>
Authored: Thu Jul 31 10:53:44 2014 -0700
Committer: James Peach <jpe...@apache.org>
Committed: Fri Aug 1 19:55:45 2014 -0700

----------------------------------------------------------------------
 cmd/traffic_manager/Makefile.am      |   10 +-
 cmd/traffic_manager/StatProcessor.cc |  359 +++++++++
 cmd/traffic_manager/StatProcessor.h  |   94 +++
 cmd/traffic_manager/StatType.cc      | 1199 +++++++++++++++++++++++++++++
 cmd/traffic_manager/StatType.h       |  231 ++++++
 cmd/traffic_manager/StatXML.cc       |   82 ++
 cmd/traffic_manager/StatXML.h        |   47 ++
 cmd/traffic_manager/stats.txt        |  236 ++++++
 configure.ac                         |    1 -
 mgmt/Makefile.am                     |    2 +-
 mgmt/stats/Makefile.am               |   40 -
 mgmt/stats/StatProcessor.cc          |  359 ---------
 mgmt/stats/StatProcessor.h           |   94 ---
 mgmt/stats/StatType.cc               | 1199 -----------------------------
 mgmt/stats/StatType.h                |  231 ------
 mgmt/stats/StatXML.cc                |   82 --
 mgmt/stats/StatXML.h                 |   47 --
 mgmt/stats/spec                      |  236 ------
 18 files changed, 2256 insertions(+), 2293 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/Makefile.am
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/Makefile.am b/cmd/traffic_manager/Makefile.am
index 1bb5614..e547f1d 100644
--- a/cmd/traffic_manager/Makefile.am
+++ b/cmd/traffic_manager/Makefile.am
@@ -27,7 +27,6 @@ AM_CPPFLAGS = \
   -I$(top_srcdir)/mgmt \
   -I$(top_srcdir)/mgmt/api/include \
   -I$(top_srcdir)/mgmt/cluster \
-  -I$(top_srcdir)/mgmt/stats \
   -I$(top_srcdir)/mgmt/utils \
   -I$(top_srcdir)/mgmt/web2 \
   -I$(top_srcdir)/lib \
@@ -36,13 +35,18 @@ AM_CPPFLAGS = \
 traffic_manager_SOURCES = \
   AddConfigFilesHere.cc \
   Main.cc \
-  Main.h
+  Main.h \
+  StatProcessor.cc \
+  StatProcessor.h \
+  StatType.cc \
+  StatType.h \
+  StatXML.cc \
+  StatXML.h
 
 traffic_manager_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @EXPAT_LDFLAGS@ 
@LIBTOOL_LINK_FLAGS@
 traffic_manager_LDADD = \
   $(top_builddir)/mgmt/libmgmt_lm.a \
   $(top_builddir)/mgmt/cluster/libcluster.a \
-  $(top_builddir)/mgmt/stats/libstats.a \
   $(top_builddir)/mgmt/web2/libweb.a \
   $(top_builddir)/mgmt/api/libmgmtapilocal.a \
   $(top_builddir)/mgmt/api/libtsmgmtshare.la \

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatProcessor.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatProcessor.cc 
b/cmd/traffic_manager/StatProcessor.cc
new file mode 100644
index 0000000..24cbaa9
--- /dev/null
+++ b/cmd/traffic_manager/StatProcessor.cc
@@ -0,0 +1,359 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/***************************************/
+/****************************************************************************
+ *
+ *  StatProcessor.cc - Functions for computing node and cluster stat
+ *                          aggregation
+ *
+ *
+ ****************************************************************************/
+
+#include "ink_config.h"
+#include "StatProcessor.h"
+#include "FileManager.h"
+
+#define STAT_CONFIG_FILE "stats.config.xml"
+
+StatObjectList statObjectList;
+StatXMLTag currentTag = INVALID_TAG;
+StatObject *statObject = NULL;
+char *exprContent = NULL;
+static unsigned statCount = 0;  // global statistics object counter
+bool nodeVar;
+bool sumClusterVar;
+
+// These helpers are used to work around the unsigned char'iness of xmlchar 
when
+// using libxml2. We don't have any tags (now) which uses UTF8.
+static int
+xml_atoi(const xmlchar *nptr)
+{
+  return atoi((const char*)nptr);
+}
+
+static double
+xml_atof(const xmlchar *nptr)
+{
+  return atof((const char*)nptr);
+}
+
+static int
+xml_strcmp(const xmlchar *s1, const char *s2)
+{
+  return strcmp((const char *)s1, s2);
+}
+
+
+static void
+elementStart(void * /* userData ATS_UNUSED */, const xmlchar *name, const 
xmlchar **atts)
+{
+  int i = 0;
+
+  if (!xml_strcmp(name, "ink:statistics"))
+    currentTag = ROOT_TAG;
+  else if (!xml_strcmp(name, "statistics"))
+    currentTag = STAT_TAG;
+  else if (!xml_strcmp(name, "destination"))
+    currentTag = DST_TAG;
+  else if (!xml_strcmp(name, "expression"))
+    currentTag = EXPR_TAG;
+  else
+    currentTag = INVALID_TAG;
+
+  switch (currentTag) {
+  case STAT_TAG:
+    statObject = new StatObject(++statCount);
+    Debug(MODULE_INIT, "\nStat #: ----------------------- %d 
-----------------------\n", statCount);
+
+    if (atts)
+     for (i = 0; atts[i]; i += 2) {
+      ink_assert(atts[i + 1]);    // Attribute comes in pairs, hopefully.
+
+      if (!xml_strcmp(atts[i], "minimum")) {
+        statObject->m_stats_min = (MgmtFloat) xml_atof(atts[i + 1]);
+        statObject->m_has_min = true;
+      } else if (!xml_strcmp(atts[i], "maximum")) {
+        statObject->m_stats_max = (MgmtFloat) xml_atof(atts[i + 1]);
+        statObject->m_has_max = true;
+      } else if (!xml_strcmp(atts[i], "interval")) {
+        statObject->m_update_interval = (ink_hrtime) xml_atoi(atts[i + 1]);
+      } else if (!xml_strcmp(atts[i], "debug")) {
+        statObject->m_debug = (atts[i + 1] && atts[i + 1][0] == '1');
+      }
+
+      Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], 
atts[i + 1]);
+    }
+    break;
+
+  case EXPR_TAG:
+    exprContent = (char*)ats_malloc(BUFSIZ * 10);
+    memset(exprContent, 0, BUFSIZ * 10);
+    break;
+
+  case DST_TAG:
+    nodeVar = true;
+    sumClusterVar = true;       // Should only be used with cluster variable
+
+    if (atts)
+     for (i = 0; atts[i]; i += 2) {
+      ink_assert(atts[i + 1]);    // Attribute comes in pairs, hopefully.
+      if (!xml_strcmp(atts[i], "scope")) {
+        nodeVar = (!xml_strcmp(atts[i + 1], "node") ? true : false);
+      } else if (!xml_strcmp(atts[i], "operation")) {
+        sumClusterVar = (!xml_strcmp(atts[i + 1], "sum") ? true : false);
+      }
+
+      Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], 
atts[i + 1]);
+    }
+
+    break;
+
+  case INVALID_TAG:
+    Debug(MODULE_INIT, "==========================================>%s<=\n", 
name);
+    break;
+
+  default:
+    break;
+  }
+}
+
+
+static void
+elementEnd(void * /* userData ATS_UNUSED */, const xmlchar */* name ATS_UNUSED 
*/)
+{
+  switch (currentTag) {
+  case STAT_TAG:
+    statObjectList.enqueue(statObject);
+    currentTag = ROOT_TAG;
+    break;
+
+  case EXPR_TAG:
+    statObject->assignExpr(exprContent); // This hands over ownership of 
exprContent
+    // fall through
+
+  default:
+    currentTag = STAT_TAG;
+    break;
+  }
+}
+
+
+static void
+charDataHandler(void * /* userData ATS_UNUSED */, const xmlchar * name, int /* 
len ATS_UNUSED */)
+{
+  if (currentTag != EXPR_TAG && currentTag != DST_TAG) {
+    return;
+  }
+
+  char content[BUFSIZ * 10];
+  if (XML_extractContent((const char*)name, content, BUFSIZ * 10) == 0) {
+    return;
+  }
+
+  if (currentTag == EXPR_TAG) {
+    ink_strlcat(exprContent, content, BUFSIZ * 10); // see above for the size
+
+  } else {
+    statObject->assignDst(content, nodeVar, sumClusterVar);
+  }
+}
+
+
+StatProcessor::StatProcessor(FileManager * configFiles):m_lmgmt(NULL), 
m_overviewGenerator(NULL)
+{
+  rereadConfig(configFiles);
+}
+
+
+void
+StatProcessor::rereadConfig(FileManager * configFiles)
+{
+  textBuffer *fileContent = NULL;
+  Rollback *fileRB = NULL;
+  char *fileBuffer = NULL;
+  version_t fileVersion;
+  int fileLen;
+
+  statObjectList.clean();
+  statCount = 0;                // reset statistics counter
+
+  int ret = configFiles->getRollbackObj(STAT_CONFIG_FILE, &fileRB);
+  if (!ret) {
+    Debug(MODULE_INIT, " Can't get Rollback for file: %s\n", STAT_CONFIG_FILE);
+  }
+  fileVersion = fileRB->getCurrentVersion();
+  fileRB->getVersion(fileVersion, &fileContent);
+  fileBuffer = fileContent->bufPtr();
+  fileLen = strlen(fileBuffer);
+
+#if HAVE_LIBEXPAT
+  /*
+   * Start the XML Praser -- the package used is EXPAT
+   */
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, NULL);
+  XML_SetElementHandler(parser, elementStart, elementEnd);
+  XML_SetCharacterDataHandler(parser, charDataHandler);
+
+  /*
+   * Substiture every newline with a space to get around
+   * the SetCharacterDataHandler problem.
+   */
+  char *newlinePtr;
+  while ((newlinePtr = strchr(fileBuffer, '\n')) != NULL || (newlinePtr = 
strchr(fileBuffer, '\r')) != NULL) {
+    *newlinePtr = ' ';
+  }
+
+  /*
+   * Parse the input file according to XML standard.
+   * Print error if we encounter any
+   */
+  int status = XML_Parse(parser, fileBuffer, fileLen, true);
+  if (!status) {
+    mgmt_log(stderr, "%s at line %d\n", 
XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
+  }
+
+  /*
+   * Cleaning upt
+   */
+  XML_ParserFree(parser);
+#else
+  /* Parse XML with libxml2 */
+  xmlSAXHandler sax;
+  memset(&sax, 0, sizeof(xmlSAXHandler));
+  sax.startElement = elementStart;
+  sax.endElement = elementEnd;
+  sax.characters = charDataHandler;
+  sax.initialized = 1;
+  xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(&sax, NULL, NULL, 0, NULL);
+
+  int status = xmlParseChunk(parser, fileBuffer, fileLen, 1);
+  if (status != 0) {
+    xmlErrorPtr errptr = xmlCtxtGetLastError(parser);
+    mgmt_log(stderr, "%s at %s:%d\n", errptr->message, errptr->file, 
errptr->line);
+  }
+  xmlFreeParserCtxt(parser);
+#endif
+
+
+  delete fileContent;
+
+  Debug(MODULE_INIT, "\n\n---------- END OF PARSING & INITIALIZING 
---------\n\n");
+}
+
+
+StatProcessor::~StatProcessor()
+{
+
+  Debug(MODULE_INIT, "[StatProcessor] Destructing Statistics Processor\n");
+
+}
+
+
+void
+setTest()
+{
+  char var_name[64];
+
+  for (int i = 1; i <= 5; i++) {
+    memset(var_name, 0, 64);
+    snprintf(var_name, sizeof(var_name), "proxy.node.stats.test%d", i);
+    if (i == 4) {
+      MgmtFloat tmp;
+      varFloatFromName("proxy.node.stats.test4", &tmp);
+      varSetFloat(var_name, tmp + 1, true);
+    } else {
+      varSetFloat(var_name, i, true);
+    }
+  }
+}
+
+
+void
+verifyTest()
+{
+  MgmtFloat tmp1, tmp2;
+
+  // 1. simple copy
+  varFloatFromName("proxy.node.stats.test1", &tmp1);
+  varFloatFromName("proxy.node.stats.test2", &tmp2);
+  if (tmp1 == tmp2) {
+    Debug(MODULE_INIT, "PASS -- simple copy");
+  } else {
+    Debug(MODULE_INIT, "FAIL -- simple copy");
+  }
+
+  // 2. simple interval
+  varFloatFromName("proxy.node.stats.test3", &tmp2);
+  if (tmp2 >= 10) {
+    Debug(MODULE_INIT, "PASS -- simple interval & constant");
+  } else {
+    Debug(MODULE_INIT, "FAIL -- simple interval & constant %f", tmp2);
+  }
+
+  // 3. delta
+  varFloatFromName("proxy.node.stats.test4", &tmp2);
+  if ((tmp2 > 150) && (tmp2 < 250)) {
+    Debug(MODULE_INIT, "PASS -- delta");
+  } else {
+    Debug(MODULE_INIT, "FAIL -- delta %f", tmp2);
+  }
+}
+
+
+/**
+ * Updating the statistics NOW.
+ **/
+unsigned short
+StatProcessor::processStat()
+{
+  unsigned short result = 0;
+
+  Debug(MODULE_INIT, "[StatProcessor] Processing Statistics....\n");
+
+//    setTest();
+  statObjectList.Eval();
+//    verifyTest();
+
+  return (result);
+}
+
+
+/**
+ * ExpressionEval
+ * --------------
+ *
+ */
+RecData
+ExpressionEval(char *exprString)
+{
+  RecDataT result_type;
+  StatObject statObject;
+
+  char content[BUFSIZ * 10];
+  XML_extractContent(exprString, content, BUFSIZ * 10);
+
+  statObject.assignExpr(content);
+  return statObject.NodeStatEval(&result_type, false);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatProcessor.h
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatProcessor.h 
b/cmd/traffic_manager/StatProcessor.h
new file mode 100644
index 0000000..71dab2f
--- /dev/null
+++ b/cmd/traffic_manager/StatProcessor.h
@@ -0,0 +1,94 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#ifndef _STAT_PROCESSOR_H_
+#define _STAT_PROCESSOR_H_
+
+/****************************************************************************
+ *
+ *  StatProcessor.h - Functions for computing node and cluster stat
+ *                          aggregation
+ *
+ *
+ ****************************************************************************/
+
+#include "ink_platform.h"
+#include <stdarg.h>
+#include "MgmtUtils.h"
+#include "MgmtDefs.h"
+#include "WebMgmtUtils.h"
+#include "ink_hrtime.h"
+#include "LocalManager.h"
+#include "WebOverview.h"
+
+#define _HEADER
+#define _D(x)
+#define _FOOTER
+#include "DynamicStats.h"
+#include "StatType.h"
+
+#if HAVE_LIBEXPAT
+#include "expat.h"
+typedef XML_Char xmlchar;
+#elif HAVE_LIBXML2
+#include <libxml/parser.h>
+#include <libxml/SAX.h>
+typedef xmlChar xmlchar;
+#else
+# error "No XML parser - please configure expat or libxml2"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+class StatProcessor
+{
+public:
+
+  explicit StatProcessor(FileManager * configFiles);
+  ~StatProcessor();
+
+  // Member Fuctions
+  unsigned short processStat();
+  void rereadConfig(FileManager * configFiles);
+
+  LocalManager *m_lmgmt;
+  overviewPage *m_overviewGenerator;
+};
+
+
+/**
+ * External expression evaluation API.
+ *
+ * INPUT: an expression string, e.g.:
+ * "(proxy.node.user_agent_total_bytes-proxy.node.origin_server_total_bytes)
+ *  / proxy.node.user_agent_total_bytes"
+ *
+ * RETURN: the resulting value of the expression.
+ * NOTE: it returns -9999.0 if there is an error.
+ *
+ */
+
+RecData ExpressionEval(char *);
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatType.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatType.cc b/cmd/traffic_manager/StatType.cc
new file mode 100644
index 0000000..1c51b95
--- /dev/null
+++ b/cmd/traffic_manager/StatType.cc
@@ -0,0 +1,1199 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/***************************************/
+/****************************************************************************
+ *
+ *  StatType.cc - Functions for computing node and cluster stat
+ *                          aggregation
+ *
+ *
+ ****************************************************************************/
+
+#include "ink_config.h"
+#include "StatType.h"
+#include "MgmtUtils.h"
+#include "ink_hrtime.h"
+#include "WebOverview.h"
+
+bool StatError = false;         // global error flag
+bool StatDebug = false;         // global debug flag
+
+/**
+ * StatExprToken()
+ * ---------------
+ */
+StatExprToken::StatExprToken()
+  : m_arith_symbol('\0'),
+    m_token_name(NULL),
+    m_token_type(RECD_NULL),
+    m_sum_var(false), m_node_var(true)
+{
+  RecDataClear(RECD_NULL, &m_token_value);
+  RecDataClear(RECD_NULL, &m_token_value_max);
+  RecDataClear(RECD_NULL, &m_token_value_min);
+  memset(&m_token_value_delta, 0, sizeof(m_token_value_delta));
+}
+
+
+/**
+ * StatExprToken::copy()
+ * ---------------------
+ */
+void
+StatExprToken::copy(const StatExprToken & source)
+{
+  m_arith_symbol = source.m_arith_symbol;
+
+  if (source.m_token_name != NULL) {
+    m_token_name = ats_strdup(source.m_token_name);
+  }
+
+  m_token_type = source.m_token_type;
+  m_token_value = source.m_token_value;
+  m_token_value_min = source.m_token_value_min;
+  m_token_value_max = source.m_token_value_max;
+
+  if (source.m_token_value_delta) {
+    m_token_value_delta = new StatDataSamples();
+    m_token_value_delta->previous_time = 
source.m_token_value_delta->previous_time;
+    m_token_value_delta->current_time = 
source.m_token_value_delta->current_time;
+    m_token_value_delta->previous_value = 
source.m_token_value_delta->previous_value;
+    m_token_value_delta->current_value = 
source.m_token_value_delta->current_value;
+  }
+
+  m_node_var = source.m_node_var;
+  m_sum_var = source.m_sum_var;
+}
+
+
+/**
+ * StatExprToken::assignTokenName()
+ * --------------------------------
+ *  Assign the token name. If the token is a predefined constant,
+ *  assign the value as well. Also, assign the token type as well.
+ */
+void
+StatExprToken::assignTokenName(const char *name)
+{
+
+  if (isdigit(name[0])) {
+    // numerical constant
+    m_token_name = ats_strdup("CONSTANT");
+    m_token_type = RECD_CONST;
+  } else {
+    m_token_name = ats_strdup(name);
+    assignTokenType();
+  }
+
+  switch (m_token_type) {
+  case RECD_INT:
+  case RECD_COUNTER:
+  case RECD_FLOAT:
+    break;
+  case RECD_CONST:
+    // assign pre-defined constant in here
+    // constant will be stored as RecFloat type.
+    if (!strcmp(m_token_name, "CONSTANT")) {
+      m_token_value.rec_float = (RecFloat) atof(name);
+    } else if (!strcmp(m_token_name, "$BYTES_TO_MB_SCALE")) {
+      m_token_value.rec_float = (RecFloat) BYTES_TO_MB_SCALE;
+    } else if (!strcmp(m_token_name, "$MBIT_TO_KBIT_SCALE")) {
+      m_token_value.rec_float = (RecFloat) MBIT_TO_KBIT_SCALE;
+    } else if (!strcmp(m_token_name, "$SECOND_TO_MILLISECOND_SCALE")) {
+      m_token_value.rec_float = (RecFloat) SECOND_TO_MILLISECOND_SCALE;
+    } else if (!strcmp(m_token_name, "$PCT_TO_INTPCT_SCALE")) {
+      m_token_value.rec_float = (RecFloat) PCT_TO_INTPCT_SCALE;
+    } else if (!strcmp(m_token_name, "$HRTIME_SECOND")) {
+      m_token_value.rec_float = (RecFloat) HRTIME_SECOND;
+    } else if (!strcmp(m_token_name, "$BYTES_TO_MBIT_SCALE")) {
+      m_token_value.rec_float = (RecFloat) BYTES_TO_MBIT_SCALE;
+    } else {
+      mgmt_log(stderr, "[StatPro] ERROR: Undefined constant: %s\n", 
m_token_name);
+      StatError = true;
+    }
+  case RECD_FX:
+  default:
+    break;
+  }
+}
+
+
+/**
+ * assignTokenType()
+ * -----------------
+ * Assign the proper token type based on the token name.
+ * Do some token type conversion if necessary. Return true
+ * if the token type is recognizable; false otherwise.
+ */
+bool StatExprToken::assignTokenType()
+{
+  ink_assert(m_token_name != NULL);
+  m_token_type = varType(m_token_name);
+
+  if (m_token_name[0] == '$') {
+    m_token_type = RECD_CONST;
+  } else if (m_token_name[0] == '_') {
+    m_token_type = RECD_FX;
+  }
+
+  if (m_token_value_delta) {
+    m_token_value_delta->data_type = m_token_type;
+  }
+
+  // I'm guessing here that we want to check if we're still RECD_NULL,
+  // it used to be INVALID, which is not in the m_token_type's enum. /leif
+  return (m_token_type != RECD_NULL);
+}
+
+
+void
+StatExprToken::clean()
+{
+  ats_free(m_token_name);
+  delete m_token_value_delta;
+}
+
+
+/**
+ * FOR DEBUGGING ONLY
+ * Print the token according to its type in a human-readable format. :)
+ */
+void
+StatExprToken::print(const char *prefix)
+{
+  if (m_token_name != NULL) {
+    printf("%s\t%s\n", prefix, m_token_name);
+  } else {
+    printf("%s\t%c\n", prefix, m_arith_symbol);
+  }
+}
+
+
+/**
+ * StatExprToken::precedence()
+ * ---------------------------
+ * Return the binary operator precedence. The higher returning value,
+ * the higher the precedence value.
+ */
+short
+StatExprToken::precedence()
+{
+  switch (m_arith_symbol) {
+  case '(':
+    return 4;
+  case '^':                    // fall through
+  case '!':
+    return 3;
+  case '*':                    // fall through
+  case '/':
+    return 2;
+  case '+':                    // fall through
+  case '-':
+    return 1;
+  default:
+    return -1;
+  }
+}
+
+
+/**
+ * StatExprToken::statVarSet()
+ * ---------------------------
+ * This method is responsible for ensuring the assigning value
+ * fall within the min. and max. bound. If it's smaller than min.
+ * or larger than max, then the error value is assigned. If no
+ * error value is assigned, either min. or max. is assigned.
+ */
+bool StatExprToken::statVarSet(RecDataT type, RecData value)
+{
+  RecData converted_value;
+
+  if (StatError) {
+    /* fix this after librecords is done
+       mgmt_log(stderr,
+       "[StatPro] ERROR in a statistics aggregation operations\n");
+     */
+    RecData err_value;
+    RecDataClear(m_token_type, &err_value);
+    return varSetData(m_token_type, m_token_name, err_value);
+  }
+
+  /*
+   * do conversion if necessary.
+   */
+  if (m_token_type != type) {
+    switch (m_token_type) {
+    case RECD_INT:
+    case RECD_COUNTER:
+      if (type == RECD_NULL)
+        converted_value = value;
+      else if (type == RECD_INT || type == RECD_COUNTER || type == RECD_FX)
+        converted_value.rec_int = value.rec_int;
+      else if (type == RECD_FLOAT || type == RECD_CONST)
+        converted_value.rec_int = (RecInt)value.rec_float;
+      else
+        Fatal("%s, invalid value type:%d\n", m_token_name, type);
+      break;
+    case RECD_FLOAT:
+      if (type == RECD_NULL)
+        converted_value = value;
+      else if (type == RECD_INT || type == RECD_COUNTER || type == RECD_FX)
+        converted_value.rec_float = (RecFloat)value.rec_int;
+      else if (type == RECD_FLOAT || type == RECD_CONST)
+        converted_value.rec_float = value.rec_float;
+      else
+        Fatal("%s, invalid value type:%d\n", m_token_name, type);
+      break;
+    default:
+      Fatal("%s, unsupported token type:%d\n", m_token_name, m_token_type);
+    }
+  } else {
+    converted_value = value;
+  }
+
+  if (RecDataCmp(m_token_type, converted_value, m_token_value_min) < 0) {
+    value = m_token_value_min;
+  }
+  else if (RecDataCmp(m_token_type, converted_value, m_token_value_max) > 0) {
+    value = m_token_value_max;
+  }
+
+  return varSetData(m_token_type, m_token_name, converted_value);
+}
+
+
+/***********************************************************************
+                                                StatExprList
+ **********************************************************************/
+
+/**
+ * StatExprList::StatExprList()
+ * ----------------------------
+ */
+StatExprList::StatExprList()
+ : m_size(0)
+{
+}
+
+
+/**
+ * StatExprList::clean()
+ * ---------------------
+ */
+void
+StatExprList::clean()
+{
+  StatExprToken *temp = NULL;
+
+  while ((temp = m_tokenList.dequeue())) {
+    delete(temp);
+    m_size -= 1;
+  }
+  ink_assert(m_size == 0);
+}
+
+
+void
+StatExprList::enqueue(StatExprToken * entry)
+{
+  ink_assert(entry);
+  m_tokenList.enqueue(entry);
+  m_size += 1;
+}
+
+
+void
+StatExprList::push(StatExprToken * entry)
+{
+  ink_assert(entry);
+  m_tokenList.push(entry);
+  m_size += 1;
+}
+
+
+StatExprToken *
+StatExprList::dequeue()
+{
+  if (m_size == 0) {
+    return NULL;
+  }
+  m_size -= 1;
+  return (StatExprToken *) m_tokenList.dequeue();
+}
+
+
+StatExprToken *
+StatExprList::pop()
+{
+  if (m_size == 0) {
+    return NULL;
+  }
+  m_size -= 1;
+  return m_tokenList.pop();
+}
+
+
+StatExprToken *
+StatExprList::top()
+{
+  if (m_size == 0) {
+    return NULL;
+  }
+  return m_tokenList.head;
+}
+
+
+StatExprToken *
+StatExprList::first()
+{
+  if (m_size == 0) {
+    return NULL;
+  }
+  return m_tokenList.head;
+}
+
+
+StatExprToken *
+StatExprList::next(StatExprToken * current)
+{
+  if (!current) {
+    return NULL;
+  }
+  return (current->link).next;
+}
+
+
+/**
+ * StatExprList::print()
+ * ---------------------
+ *  Print the token in the expression in a human-readable format. :)
+ */
+void
+StatExprList::print(const char *prefix)
+{
+  for (StatExprToken * token = first(); token; token = next(token)) {
+    token->print(prefix);
+  }
+}
+
+
+/**
+ * StatExprToken::count()
+ * ----------------------
+ * Counts the number of token in the expression list and return it.
+ */
+unsigned
+StatExprList::count()
+{
+  return m_size;
+}
+
+
+/***********************************************************************
+                                                    StatObject
+ **********************************************************************/
+
+
+/**
+ * StatObject::StatObject()
+ * ------------------------
+ */
+
+StatObject::StatObject()
+ : m_id(1),
+   m_debug(false),
+   m_expr_string(NULL),
+   m_node_dest(NULL),
+   m_cluster_dest(NULL),
+   m_expression(NULL),
+   m_postfix(NULL),
+   m_last_update(-1),
+   m_current_time(-1), m_update_interval(-1),
+   m_stats_max(FLT_MAX), m_stats_min(FLT_MIN),
+   m_has_max(false), m_has_min(false), m_has_delta(false)
+{
+}
+
+
+StatObject::StatObject(unsigned identifier)
+  : m_id(identifier),
+    m_debug(false),
+    m_expr_string(NULL),
+    m_node_dest(NULL),
+    m_cluster_dest(NULL),
+    m_expression(NULL),
+    m_postfix(NULL),
+    m_last_update(-1),
+    m_current_time(-1), m_update_interval(-1),
+    m_stats_max(FLT_MAX), m_stats_min(FLT_MIN),
+    m_has_max(false), m_has_min(false), m_has_delta(false)
+{
+}
+
+
+/**
+ * StatObject::clean()
+ * -------------------
+ */
+void
+StatObject::clean()
+{
+  ats_free(m_expr_string);
+  delete m_node_dest;
+  delete m_cluster_dest;
+  delete m_postfix;
+}
+
+
+/**
+ * StatObject::assignDst()
+ * -----------------------
+ */
+void
+StatObject::assignDst(const char *str, bool m_node_var, bool m_sum_var)
+{
+  if (StatDebug) {
+    Debug(MODULE_INIT, "DESTINTATION: %s\n", str);
+  }
+
+  StatExprToken *statToken = new StatExprToken();
+
+  statToken->assignTokenName(str);
+  statToken->m_node_var = m_node_var;
+  statToken->m_sum_var = m_sum_var;
+
+  // The type of dst token should be always not NULL
+  if (statToken->m_token_type == RECD_NULL) {
+    Fatal("token:%s, invalid token type!", statToken->m_token_name);
+  }
+
+  // Set max/min value
+  if (m_has_max)
+    RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_max,
+                        m_stats_max);
+  else
+    RecDataSetMax(statToken->m_token_type, &statToken->m_token_value_max);
+
+  if (m_has_min)
+    RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_min,
+                        m_stats_min);
+  else
+    RecDataSetMin(statToken->m_token_type, &statToken->m_token_value_min);
+
+  if (m_node_var) {
+    ink_assert(m_node_dest == NULL);
+    m_node_dest = statToken;
+  } else {
+    ink_assert(m_cluster_dest == NULL);
+    m_cluster_dest = statToken;
+  }
+}
+
+
+/**
+ * StatObject::assignExpr()
+ * ------------------------
+ */
+void
+StatObject::assignExpr(char *str)
+{
+  StatExprToken *statToken = NULL;
+
+  if (StatDebug) {
+    Debug(MODULE_INIT, "EXPRESSION: %s\n", str);
+  }
+  ink_assert(m_expr_string == NULL);
+  // We take ownership here
+  m_expr_string = str;
+
+  Tokenizer exprTok(" ");
+  exprTok.Initialize(str);
+  tok_iter_state exprTok_state;
+  const char *token = exprTok.iterFirst(&exprTok_state);
+
+  ink_assert(m_expression == NULL);
+  m_expression = new StatExprList();
+
+  while (token) {
+
+    statToken = new StatExprToken();
+
+    if (isOperator(token[0])) {
+
+      statToken->m_arith_symbol = token[0];
+      ink_assert(statToken->m_token_name == NULL);
+
+      if (StatDebug) {
+        Debug(MODULE_INIT, "\toperator: ->%c<-\n", statToken->m_arith_symbol);
+      }
+
+    } else {
+
+      ink_assert(statToken->m_arith_symbol == '\0');
+
+      // delta
+      if (token[0] == '#') {
+
+        token += 1;             // skip '#'
+        statToken->m_token_value_delta = new StatDataSamples();
+        statToken->m_token_value_delta->previous_time = (ink_hrtime) 0;
+        statToken->m_token_value_delta->current_time = (ink_hrtime) 0;
+        statToken->m_token_value_delta->data_type = RECD_NULL;
+        RecDataClear(RECD_NULL, 
&statToken->m_token_value_delta->previous_value);
+        RecDataClear(RECD_NULL, 
&statToken->m_token_value_delta->current_value);
+
+      }
+
+      statToken->assignTokenName(token);
+
+      if (StatDebug) {
+        Debug(MODULE_INIT, "\toperand:  ->%s<-\n", token);
+      }
+
+    }
+
+    token = exprTok.iterNext(&exprTok_state);
+    m_expression->enqueue(statToken);
+
+  }
+
+  infix2postfix();
+
+}
+
+
+/**
+ * StatObject::infix2postfix()
+ * ---------------------------
+ * Takes the infix "expression" and convert it to a postfix for future
+ * evaluation.
+ *
+ * SIDE EFFECT: consume all token in "expression"
+ */
+void
+StatObject::infix2postfix()
+{
+  StatExprList stack;
+  StatExprToken *tempToken = NULL;
+  StatExprToken *curToken = NULL;
+  m_postfix = new StatExprList();
+
+  while (m_expression->top()) {
+    curToken = m_expression->dequeue();
+
+    if (!isOperator(curToken->m_arith_symbol)) {
+      //printf("I2P~: enqueue %s\n", curToken->m_token_name);
+      m_postfix->enqueue(curToken);
+
+    } else {
+      ink_assert(curToken->m_arith_symbol != '\0');
+
+      if (curToken->m_arith_symbol == '(') {
+        stack.push(curToken);
+      } else if (curToken->m_arith_symbol == ')') {
+        tempToken = (StatExprToken *) stack.pop();
+
+        while (tempToken->m_arith_symbol != '(') {
+          //printf("I2P@: enqueue %c\n", tempToken->m_arith_symbol);
+          m_postfix->enqueue(tempToken);
+          tempToken = (StatExprToken *) stack.pop();
+        }
+
+        // Free up memory for ')'
+        delete(curToken);
+        delete(tempToken);
+
+      } else {
+        if (stack.count() == 0) {
+          stack.push(curToken);
+        } else {
+          tempToken = (StatExprToken *) stack.top();
+
+          while ((tempToken->m_arith_symbol != '(') && 
(tempToken->precedence() >= curToken->precedence())) {
+            tempToken = (StatExprToken *) stack.pop();  // skip the (
+            //printf("I2P$: enqueue %c\n", tempToken->m_arith_symbol);
+            m_postfix->enqueue(tempToken);
+            if (stack.count() == 0) {
+              break;
+            }
+            tempToken = (StatExprToken *) stack.top();
+          }                     // while
+
+          stack.push(curToken);
+        }
+      }
+    }
+  }
+
+  while (stack.count() > 0) {
+    tempToken = (StatExprToken *) stack.pop();
+    //printf("I2P?: enqueue %c\n", tempToken->m_arith_symbol);
+    m_postfix->enqueue(tempToken);
+  }
+
+  // dump infix expression
+  delete(m_expression);
+  m_expression = NULL;
+}
+
+
+/**
+ * StatObject::NodeStatEval()
+ * --------------------------
+ *
+ *
+ */
+RecData StatObject::NodeStatEval(RecDataT *result_type, bool cluster)
+{
+  StatExprList stack;
+  StatExprToken *left = NULL;
+  StatExprToken *right = NULL;
+  StatExprToken *result = NULL;
+  StatExprToken *curToken = NULL;
+  RecData tempValue;
+  RecDataClear(RECD_NULL, &tempValue);
+
+  *result_type = RECD_NULL;
+
+  /* Express checkout lane -- Stat. object with on 1 source variable */
+  if (m_postfix->count() == 1) {
+    StatExprToken * src = m_postfix->top();
+
+    // in librecords, not all statistics are register at initialization
+    // must assign proper type if it is undefined.
+    if (src->m_token_type == RECD_NULL) {
+      src->assignTokenType();
+    }
+
+    *result_type = src->m_token_type;
+    if (src->m_token_type == RECD_CONST) {
+      tempValue = src->m_token_value;
+    } else if (src->m_token_value_delta) {
+      tempValue = src->m_token_value_delta->diff_value(src->m_token_name);
+    } else if (!cluster) {
+      if (!varDataFromName(src->m_token_type, src->m_token_name, &tempValue)) {
+        RecDataClear(src->m_token_type, &tempValue);
+      }
+    } else {
+      if (!overviewGenerator->varClusterDataFromName(src->m_token_type,
+                                                     src->m_token_name,
+                                                     &tempValue)) {
+        RecDataClear(src->m_token_type, &tempValue);
+      }
+    }
+  } else {
+
+    /* standard postfix evaluation */
+    for (StatExprToken * token = m_postfix->first(); token; token = 
m_postfix->next(token)) {
+      /* carbon-copy the token. */
+      curToken = new StatExprToken();
+      curToken->copy(*token);
+
+      if (!isOperator(curToken->m_arith_symbol)) {
+        stack.push(curToken);
+      } else {
+        ink_assert(isOperator(curToken->m_arith_symbol));
+        right = stack.pop();
+        left = stack.pop();
+
+        if (left->m_token_type == RECD_NULL) {
+          left->assignTokenType();
+        }
+        if (right->m_token_type == RECD_NULL) {
+          right->assignTokenType();
+        }
+
+        result = StatBinaryEval(left, curToken->m_arith_symbol, right, 
cluster);
+
+        stack.push(result);
+        delete(curToken);
+        delete(left);
+        delete(right);
+      }
+    }
+
+    /* should only be 1 value left on stack -- the resulting value */
+    if (stack.count() > 1) {
+      stack.print("\t");
+      ink_assert(false);
+    }
+
+    *result_type = stack.top()->m_token_type;
+    tempValue = stack.top()->m_token_value;
+  }
+
+  return tempValue;
+
+}
+
+
+/**
+ * StatObject::ClusterStatEval()
+ * -----------------------------
+ *
+ *
+ */
+RecData StatObject::ClusterStatEval(RecDataT *result_type)
+{
+  /* Sanity check */
+  ink_assert(m_cluster_dest && !m_cluster_dest->m_node_var);
+
+  // what is this?
+  if ((m_node_dest == NULL) || (m_cluster_dest->m_sum_var == false)) {
+    return NodeStatEval(result_type, true);
+  } else {
+    RecData tempValue;
+
+    if (!overviewGenerator->varClusterDataFromName(m_node_dest->m_token_type,
+                                                   m_node_dest->m_token_name,
+                                                   &tempValue)) {
+      *result_type = RECD_NULL;
+      RecDataClear(*result_type, &tempValue);
+    }
+
+    return (tempValue);
+  }
+}
+
+
+/**
+ * StatObject::setTokenValue()
+ * ---------------------------
+ * The logic of the following code segment is the following.
+ * The objective is to extract the appropriate right->m_token_value.
+ * If 'right' is an intermediate value, nothing to do.
+ * If m_token_type is RECD_CONST, nothing to do.
+ * If m_token_type is RECD_FX, right->m_token_value is the diff. in time.
+ * If m_token_type is either RECD_INT or RECD_FLOAT, it can either
+ * by a cluster variable or a node variable.
+ *     If it is a cluster variable, just use varClusterFloatFromName
+ *     to set right->m_token_value.
+ *     If it is a node variable, then it can either be a variable
+ *     with delta. To determine whether it has a delta, simply search
+ *     the m_token_name in the delta list. If found then it has delta.
+ *     If it has delta then use the delta's diff. in value,
+ *     otherwise simply set right->m_token_value with varFloatFromName.
+ */
+void
+StatObject::setTokenValue(StatExprToken * token, bool cluster)
+{
+  if (token->m_token_name) {
+    // it is NOT an intermediate value
+
+    switch (token->m_token_type) {
+    case RECD_CONST:
+      break;
+
+    case RECD_FX:
+      // only support time function
+      // use rec_int to store time value
+      token->m_token_value.rec_int = (m_current_time - m_last_update);
+      break;
+
+    case RECD_INT:             // fallthought
+    case RECD_COUNTER:
+    case RECD_FLOAT:
+      if (cluster) {
+        if (!overviewGenerator->varClusterDataFromName(token->m_token_type,
+                                                       token->m_token_name,
+                                                       
&(token->m_token_value)))
+        {
+          RecDataClear(token->m_token_type, &token->m_token_value);
+        }
+      } else {
+        if (token->m_token_value_delta) {
+          token->m_token_value =
+            token->m_token_value_delta->diff_value(token->m_token_name);
+        } else {
+          if (!varDataFromName(token->m_token_type, token->m_token_name,
+                               &(token->m_token_value))) {
+            RecDataClear(token->m_token_type, &token->m_token_value);
+          }
+        }                       // delta?
+      }                         // cluster?
+      break;
+
+    default:
+      if (StatDebug) {
+        Debug(MODULE, "Unrecognized token \"%s\" of type %d.\n",
+              token->m_token_name, token->m_token_type);
+      }
+    }                           // switch
+  }                             // m_token_name?
+}
+
+
+/**
+ * StatObject::StatBinaryEval()
+ * ------------------------
+ * Take the left token, the right token, an binary operation and perform an
+ * arithmatic operations on them. This function is responsible for getting the
+ * correct value from:
+ * - (1) node variable
+ * - (2) node variable with a delta structure
+ * - (3) cluster variable
+ * - (4) an immediate value
+ */
+StatExprToken *StatObject::StatBinaryEval(StatExprToken * left, char op,
+                                          StatExprToken * right, bool cluster)
+{
+  RecData l, r;
+  StatExprToken *result = new StatExprToken();
+  result->m_token_type = RECD_INT;
+
+  if (left->m_token_type == RECD_NULL
+      && right->m_token_type == RECD_NULL) {
+    return result;
+  }
+
+  if (left->m_token_type != RECD_NULL) {
+    setTokenValue(left, cluster);
+    result->m_token_type = left->m_token_type;
+  }
+
+  if (right->m_token_type != RECD_NULL) {
+    setTokenValue(right, cluster);
+    switch (result->m_token_type) {
+    case RECD_NULL:
+      result->m_token_type = right->m_token_type;
+      break;
+    case RECD_FX:
+    case RECD_INT:
+    case RECD_COUNTER:
+      /*
+       * When types of left and right are different, select RECD_FLOAT
+       * as result type. It's may lead to loss of precision when do
+       * conversion, be careful!
+       */
+      if (right->m_token_type == RECD_FLOAT
+          || right->m_token_type == RECD_CONST) {
+        result->m_token_type = right->m_token_type;
+      }
+      break;
+    case RECD_CONST:
+    case RECD_FLOAT:
+      break;
+    default:
+      Fatal("Unexpected RecData Type:%d", result->m_token_type);
+      break;
+    }
+  }
+
+  /*
+   * We should make the operands with the same type before calculating.
+   */
+  RecDataClear(RECD_NULL, &l);
+  RecDataClear(RECD_NULL, &r);
+
+  if (left->m_token_type == right->m_token_type ) {
+    l = left->m_token_value;
+    r = right->m_token_value;
+  } else if (result->m_token_type != left->m_token_type) {
+    if (left->m_token_type != RECD_NULL) {
+      ink_assert(result->m_token_type == RECD_FLOAT
+                 || result->m_token_type == RECD_CONST);
+
+      l.rec_float = (RecFloat)left->m_token_value.rec_int;
+    }
+    r = right->m_token_value;
+    ink_assert(result->m_token_type == right->m_token_type);
+  } else {
+    l = left->m_token_value;
+    if (right->m_token_type != RECD_NULL) {
+      ink_assert(result->m_token_type == RECD_FLOAT
+                 || result->m_token_type == RECD_CONST);
+
+      r.rec_float = (RecFloat)right->m_token_value.rec_int;
+    }
+    ink_assert(result->m_token_type == left->m_token_type);
+  }
+
+  /*
+   * Start to calculate
+   */
+  switch (op) {
+  case '+':
+    result->m_token_value = RecDataAdd(result->m_token_type, l, r);
+    break;
+
+  case '-':
+    result->m_token_value = RecDataSub(result->m_token_type, l, r);
+    break;
+
+  case '*':
+    result->m_token_value = RecDataMul(result->m_token_type, l, r);
+    break;
+
+  case '/':
+    RecData recTmp;
+    RecDataClear(RECD_NULL, &recTmp);
+
+    /*
+     * Force the type of result to be RecFloat on div operation
+     */
+    if (result->m_token_type != RECD_FLOAT && result->m_token_type != 
RECD_CONST) {
+      RecFloat t;
+
+      result->m_token_type = RECD_FLOAT;
+
+      t = (RecFloat)l.rec_int;
+      l.rec_float = t;
+
+      t = (RecFloat)r.rec_int;
+      r.rec_float = t;
+    }
+
+    if (RecDataCmp(result->m_token_type, r, recTmp)) {
+      result->m_token_value = RecDataDiv(result->m_token_type, l, r);
+    }
+    break;
+
+  default:
+    // should never reach here
+    StatError = true;
+  }
+
+  return (result);
+}
+
+
+/***********************************************************************
+                                                  StatObjectList
+ **********************************************************************/
+
+StatObjectList::StatObjectList()
+ : m_size(0)
+{
+}
+
+
+void
+StatObjectList::clean()
+{
+  StatObject *temp = NULL;
+
+  while ((temp = m_statList.dequeue())) {
+    m_size -= 1;
+    delete(temp);
+  }
+
+  ink_assert(m_size == 0);
+}
+
+
+void
+StatObjectList::enqueue(StatObject * object)
+{
+  for (StatExprToken * token = object->m_postfix->first(); token; token = 
object->m_postfix->next(token)) {
+    if (token->m_token_value_delta) {
+      object->m_has_delta = true;
+      break;
+    }
+  }
+
+  m_statList.enqueue(object);
+  m_size += 1;
+}
+
+
+StatObject *
+StatObjectList::first()
+{
+  return m_statList.head;
+}
+
+
+StatObject *
+StatObjectList::next(StatObject * current)
+{
+  return (current->link).next;
+}
+
+
+/**
+ * StatObjectList::Eval()
+ * ----------------------
+ * The statisitic processor entry point to perform the calculation.
+ */
+short
+StatObjectList::Eval()
+{
+  RecData tempValue;
+  RecData result;
+  RecDataT result_type;
+  ink_hrtime threshold = 0;
+  ink_hrtime delta = 0;
+  short count = 0;
+
+  RecDataClear(RECD_NULL, &tempValue);
+  RecDataClear(RECD_NULL, &result);
+
+  for (StatObject * object = first(); object; object = next(object)) {
+    StatError = false;
+    StatDebug = object->m_debug;
+
+    if (StatDebug) {
+      Debug(MODULE, "\n##### %d #####\n", object->m_id);
+    }
+
+    if (object->m_update_interval <= 0) {
+      // non-time statistics
+      object->m_current_time = ink_get_hrtime_internal();
+
+      if (object->m_node_dest) {
+        result = object->NodeStatEval(&result_type, false);
+        object->m_node_dest->statVarSet(result_type, result);
+      }
+
+      if (object->m_cluster_dest) {
+        result = object->ClusterStatEval(&result_type);
+        object->m_cluster_dest->statVarSet(result_type, result);
+      }
+
+      object->m_last_update = object->m_current_time;
+    } else {
+      // timed statisitics
+      object->m_current_time = ink_get_hrtime_internal();
+
+      threshold = object->m_update_interval * HRTIME_SECOND;
+      delta = object->m_current_time - object->m_last_update;
+
+      if (StatDebug) {
+        Debug(MODULE, "\tUPDATE:%" PRId64 " THRESHOLD:%" PRId64 ", DELTA:%" 
PRId64 "\n", object->m_update_interval, threshold, delta);
+      }
+
+      /* Should we do the calculation? */
+      if ((delta > threshold) ||        /* sufficient elapsed time? */
+          (object->m_last_update == -1) ||      /*       first time?       */
+          (object->m_last_update > object->m_current_time)) {   /*wrapped */
+
+        if (StatDebug) {
+          if (delta > threshold) {
+            Debug(MODULE, "\t\tdelta > threshold IS TRUE!\n");
+          }
+          if (object->m_last_update == -1) {
+            Debug(MODULE, "\t\tm_last_update = -1 IS TRUE!\n");
+          }
+          if (object->m_last_update > object->m_current_time) {
+            Debug(MODULE, "\t\tm_last_update > m_current_time IS TRUE\n");
+          }
+        }
+
+        if (!object->m_has_delta) {
+
+          if (StatDebug) {
+            Debug(MODULE, "\tEVAL: Simple time-condition.\n");
+          }
+
+          if (object->m_node_dest) {
+            result = object->NodeStatEval(&result_type, false);
+            object->m_node_dest->statVarSet(result_type, result);
+          }
+
+          if (object->m_cluster_dest) {
+            result = object->ClusterStatEval(&result_type);
+            object->m_cluster_dest->statVarSet(result_type, result);
+          }
+
+          object->m_last_update = object->m_current_time;
+        } else {
+          /* has delta */
+          if (StatDebug) {
+            Debug(MODULE, "\tEVAL: Complicated time-condition.\n");
+          }
+          // scroll old values
+          for (StatExprToken * token = object->m_postfix->first(); token; 
token = object->m_expression->next(token)) {
+
+            // in librecords, not all statistics are register at initialization
+            // must assign proper type if it is undefined.
+            if (!isOperator(token->m_arith_symbol) && token->m_token_type == 
RECD_NULL) {
+              token->assignTokenType();
+            }
+
+            if (token->m_token_value_delta) {
+              if (!varDataFromName(token->m_token_type, token->m_token_name,
+                                   &tempValue)) {
+                RecDataClear(RECD_NULL, &tempValue);
+              }
+
+              token->m_token_value_delta->previous_time = 
token->m_token_value_delta->current_time;
+              token->m_token_value_delta->previous_value = 
token->m_token_value_delta->current_value;
+              token->m_token_value_delta->current_time = 
object->m_current_time;
+              token->m_token_value_delta->current_value = tempValue;
+            }
+          }
+
+          if (delta > threshold) {
+            if (object->m_node_dest) {
+              result = object->NodeStatEval(&result_type, false);
+              object->m_node_dest->statVarSet(result_type, result);
+            }
+
+            if (object->m_cluster_dest) {
+              result = object->ClusterStatEval(&result_type);
+              object->m_cluster_dest->statVarSet(result_type, result);
+            }
+
+            object->m_last_update = object->m_current_time;
+          } else {
+            if (StatDebug) {
+              Debug(MODULE, "\tEVAL: Timer not expired, do nothing\n");
+            }
+          }
+        }                       /* delta? */
+      } else {
+        if (StatDebug) {
+          Debug(MODULE, "\tEVAL: Timer not expired, nor 1st time, nor wrapped, 
SORRY!\n");
+        }
+      }                         /* timed event */
+    }
+    count += 1;
+  }                             /* for */
+
+  return count;
+}                               /* Eval() */
+
+
+/**
+ * StatObjectList::print()
+ * --------------------------
+ * Print the list of of statistics object in a human-readable format. :)
+ */
+void
+StatObjectList::print(const char *prefix)
+{
+  for (StatObject * object = first(); object; object = next(object)) {
+    if (StatDebug) {
+      Debug(MODULE, "\n%sSTAT OBJECT#: %d\n", prefix, object->m_id);
+    }
+
+    if (object->m_expression) {
+      object->m_expression->print("\t");
+    }
+
+    if (object->m_postfix) {
+      object->m_postfix->print("\t");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatType.h
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatType.h b/cmd/traffic_manager/StatType.h
new file mode 100644
index 0000000..673e1e7
--- /dev/null
+++ b/cmd/traffic_manager/StatType.h
@@ -0,0 +1,231 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/***************************************/
+/****************************************************************************
+ *
+ *  StatType.h - Functions for computing node and cluster stat
+ *                          aggregation
+ *
+ *
+ ****************************************************************************/
+
+#ifndef _STATTYPE_H_
+#define        _STATTYPE_H_
+
+#include "StatXML.h"
+#include "Main.h"               // Debug()
+#include "WebMgmtUtils.h"
+
+#define BYTES_TO_MBIT_SCALE (8/1000000.0)
+
+/* Structs used in Average Statistics calculations */
+struct StatDataSamples
+{
+  ink_hrtime previous_time;
+  ink_hrtime current_time;
+  RecDataT data_type;
+  RecData previous_value;
+  RecData current_value;
+
+  RecData diff_value(const char *name)
+  {
+    RecData tmp;
+
+    if (data_type == RECD_NULL) {
+      data_type = varType(name);
+    }
+
+    if (data_type != RECD_NULL)
+      return RecDataSub(data_type, current_value, previous_value);
+    else {
+      RecDataClear(RECD_NULL, &tmp);
+      return tmp;
+    }
+  }
+  ink_hrtime diff_time()
+  {
+    return (current_time - previous_time);
+  }
+};
+
+// Urgly workaround -- no optimization in HPUX
+#if defined(hpux)
+#define inline
+#endif
+
+#define MODULE      "StatPro"   // Statistics processor debug tag
+#define MODULE_INIT "StatProInit"       // Statistics processor debug tag
+
+/***************************************************************
+ *                       StatExprToken
+ * a statistics expression token can either be a binary operator,
+ * name '+', '-', '*', '/', or parenthesis '(', ')' or a TS variable.
+ * In the former case, the arithSymbol stores the operator or
+ * paranthesis; otherwise arithSymbol is '/0';
+ ***************************************************************/
+class StatExprToken
+{
+
+public:
+
+  char m_arith_symbol;
+  char *m_token_name;
+  RecDataT m_token_type;
+  RecData m_token_value;
+  RecData m_token_value_max;
+  RecData m_token_value_min;
+  StatDataSamples *m_token_value_delta;
+  bool m_sum_var;
+  bool m_node_var;
+
+  // Member Functions
+  void assignTokenName(const char *);
+  bool assignTokenType();
+  void print(const char *);
+  short precedence();
+  void copy(const StatExprToken &);
+
+  LINK(StatExprToken, link);
+  StatExprToken();
+  inline ~ StatExprToken()
+  {
+    clean();
+  };
+  void clean();
+
+  bool statVarSet(RecDataT, RecData);
+};
+
+
+/**
+ * StatExprList
+ *   simply a list of StatExprToken.
+ **/
+class StatExprList
+{
+
+public:
+
+  StatExprList();
+  inline ~ StatExprList()
+  {
+    clean();
+  };
+  void clean();
+
+  void enqueue(StatExprToken *);
+  void push(StatExprToken *);
+  StatExprToken *dequeue();
+  StatExprToken *pop();
+  StatExprToken *top();
+  StatExprToken *first();
+  StatExprToken *next(StatExprToken *);
+  unsigned count();
+  void print(const char *);
+
+private:
+
+  size_t m_size;
+  Queue<StatExprToken> m_tokenList;
+};
+
+/***************************************************************
+ *                        StatObject
+ * Each entry in the statistics XML file is represented by a
+ * StatObject.
+ ***************************************************************/
+class StatObject
+{
+
+public:
+
+  unsigned m_id;
+  bool m_debug;
+  char *m_expr_string;          /* for debugging using only */
+  StatExprToken *m_node_dest;
+  StatExprToken *m_cluster_dest;
+  StatExprList *m_expression;
+  StatExprList *m_postfix;
+  ink_hrtime m_last_update;
+  ink_hrtime m_current_time;
+  ink_hrtime m_update_interval;
+  RecFloat m_stats_max;
+  RecFloat m_stats_min;
+  bool m_has_max;
+  bool m_has_min;
+  bool m_has_delta;
+  LINK(StatObject, link);
+
+  // Member functions
+  StatObject();
+  StatObject(unsigned);
+  inline ~ StatObject()
+  {
+    clean();
+  };
+  void clean();
+  void assignDst(const char *, bool, bool);
+  void assignExpr(char *);
+
+  StatExprToken *StatBinaryEval(StatExprToken *, char, StatExprToken *, bool 
cluster = false);
+  RecData NodeStatEval(RecDataT *result_type, bool cluster);
+  RecData ClusterStatEval(RecDataT *result_type);
+  void setTokenValue(StatExprToken *, bool cluster = false);
+
+private:
+
+  void infix2postfix();
+};
+
+
+/**
+ * StatObjectList
+ *    simply a list of StatObject.
+ **/
+class StatObjectList
+{
+
+public:
+
+  // Member functions
+  StatObjectList();
+  inline ~ StatObjectList()
+  {
+    clean();
+  };
+  void clean();
+  void enqueue(StatObject * object);
+  StatObject *first();
+  StatObject *next(StatObject * current);
+  void print(const char *prefix = "");
+  short Eval();                 // return the number of statistics object 
processed
+
+  size_t m_size;
+
+private:
+
+  Queue<StatObject> m_statList;
+};
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatXML.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatXML.cc b/cmd/traffic_manager/StatXML.cc
new file mode 100644
index 0000000..277b84a
--- /dev/null
+++ b/cmd/traffic_manager/StatXML.cc
@@ -0,0 +1,82 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "ink_config.h"
+#include "StatXML.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+//
+// Extract the text between a pair of XML tag and returns the length
+// of the extracted text.
+//
+unsigned short
+XML_extractContent(const char *name, char *content, size_t result_len)
+{
+
+  char c;
+  int contentIndex = 0;
+
+  memset(content, 0, result_len);
+  for (unsigned short nameIndex = 0; name[nameIndex] != '<'; nameIndex += 1) {
+    c = name[nameIndex];
+
+    if (isspace(c)) {
+      continue;
+    }
+
+    if (isOperator(c)) {
+      content[contentIndex++] = ' ';
+      content[contentIndex++] = c;
+      content[contentIndex++] = ' ';
+    } else {
+      content[contentIndex++] = c;
+    }
+  }
+
+  return (strlen(content));
+
+}
+
+
+//
+// Returns true  if 'c'is an operator (in our definition),
+//         false otherwise
+//
+bool
+isOperator(char c)
+{
+
+  switch (c) {
+  case '+':
+  case '-':
+  case '*':
+  case '/':
+  case '(':
+  case ')':
+    return true;
+  default:
+    return false;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/StatXML.h
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/StatXML.h b/cmd/traffic_manager/StatXML.h
new file mode 100644
index 0000000..b31063b
--- /dev/null
+++ b/cmd/traffic_manager/StatXML.h
@@ -0,0 +1,47 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+
+#ifndef _STATXML_H_
+#define        _STATXML_H_
+
+#include "WebMgmtUtils.h"
+#include "List.h"
+
+typedef enum
+{
+  INVALID_TAG = -1,
+  ROOT_TAG,
+  STAT_TAG,
+  DST_TAG,
+  EXPR_TAG
+} StatXMLTag;
+
+/***************************************************************
+ *                      General Methods
+ ***************************************************************/
+bool isOperator(char);
+int XML_getContent(const char *, int, char *, StatXMLTag);
+unsigned short XML_extractContent(const char *, char *, size_t);
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/cmd/traffic_manager/stats.txt
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/stats.txt b/cmd/traffic_manager/stats.txt
new file mode 100644
index 0000000..97bdb90
--- /dev/null
+++ b/cmd/traffic_manager/stats.txt
@@ -0,0 +1,236 @@
+//-----------------------------------------------------------------------------
+//
+// Statistics Processor
+//
+// Last Update: 04/11/2001
+//
+//-----------------------------------------------------------------------------
+// -*- coding: utf-8 -*-
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+//-----------------------------------------------------------------------------
+// Design
+//-----------------------------------------------------------------------------
+
+This statistics processor is a Cougar II (Tsunami) feature. 
+It is designed to replace the StatAggregate.cc and portion of WebOverview.cc 
+for scalability, maintainability, and ability to customize reasons.
+
+The statistics processor aggregate/calculate traffic server statistics based
+on a XML-based configuration file. At the processor's initialization, the 
+configuration file is read and parsed. Each set of calculation is then stored
+as a C++ object, called StatObject; and each StatObject is linked into a list,
+called StatObjectList.
+
+The mgmt/traffic_manager threads will invoke the StatObjectList->Eval() to
+perform the statistics aggregation and calculation within its event loop. As
+Eval() is call, each StatObject in StatObjectList is evaluated.
+
+Recall: StatAggregate.cc aggregates/calculates/copies proxy.process.* TS
+variables to proxy.node.* variables. Similarly, WebOverview.cc aggregate
+proxy.node.* variables to their corresponding proxy.cluster.* variable.
+
+So there are two types of calculations in the statistics processor: NodeEval
+and ClusterEval. As their names imply, they aggregate node-based statistics
+and clsuter-based statistics, respectively. We call the different basis of 
+statistics aggregation as "scope". (See "Destination Attributes")
+
+In the cluster-based statistics, the aggregation is further divided into two
+types: sum and re-calculate. Sum refers calculating the proxy.cluster.* 
+variable by simply summing all required proxy.node.* variables from nodes in
+the cluster. Re-calculate refers to summing all proxy.nodes.* variables that 
+are used in the process of calculation before performing the calculation. 
+An analogy would be, summing all open connection in the cluster vs. the 
+average hit rate in the cluster.
+
+//-----------------------------------------------------------------------------
+// Destination Attributes
+//-----------------------------------------------------------------------------
+       
+       "scope" 
+       - "node"
+       - "cluster"
+
+       "operation"
+       - "sum"
+               summing the corresponding node variable across all nodes in the 
cluster.
+       - "re-calculate"
+               
+
+       "define"
+       - "custom"
+       - "built-in"
+
+//-----------------------------------------------------------------------------
+// Predefined Constants and Functions
+//-----------------------------------------------------------------------------
+       
+       Predefined Constants
+
+       . BYTES_TO_MB_SCALE (1/(1024*1024.0))
+         - convert bytes to mega-bytes
+
+       . MBIT_TO_KBIT_SCALE (1000.0)
+         - convert mega-bits to kilo-bits
+
+       . SECOND_TO_MILLISECOND_SCALE (1000.0)
+         - convert seconds to milliseconds
+
+       . PCT_TO_INTPCT_SCALE (100.0)
+         - convert ratios to percentage
+
+       . HRTIME_SECOND
+         - converting milli-seconds to seconds
+
+       . BYTES_TO_MBIT_SCALE (8/1000000.0)
+         - convert bytes to mega-bits
+
+       Predefined Functions
+       . DIFFTIME
+         - the number of milliseconds since last update. Usually used in 
+        combination of HRTIME_SECOND which computes the number of seconds
+        since last update.
+
+//-----------------------------------------------------------------------------
+// Unit test plan
+//-----------------------------------------------------------------------------
+
+The statistics processor is designed to replace StatAggregate.cc and part of
+the WebOverview. The first thing to test StatProcessor is to comment the 
+aggregateNodeRecords() and doClusterAg() calls from mgmt/Main.cc.
+
+The next step is to replace the above function calls with StatProcessor::
+processStat().
+
+This statistics processor is a rather complicated module in traffic manager.
+Hence it can't be easily tested. We divided the test into multiple sections.
+
+1) Node-based Simple Aggregation
+       - simply performs those aggregation that are node-based and the 
aggregation
+      is performed every time statProcess() is invoked.
+         E.g.: hit rate = doc. hit / doc. served.
+
+2) Node-based Time-Delta Aggregation
+       - performs those aggregation that are node-based but the operation is 
only
+      perform in a regular interval AND one of more variables in the 
+      calculation is obtained by calculating the difference between the last
+      updated value and the current value. E.g. average connections per second
+      is calculated by subtracting the 10 seconds ago connection count from the
+      current connection count and divide the quotient by 10.
+
+Repeat the about 2 testes with cluster-based variables. So, we have, at least,
+4 test cases.
+
+Developing a PASS/FAIL unit test that will test the statistics processor is 
not 
+cost-efficient. The approach we are going to use is to display the input value
+and the output value before and after the calculation is done.
+
+Let's subdivide the testes in two stages:
+
+Stage 1 : Synthetic Data
+------------------------
+We control the testing environment by setting the input values. This will test
+the correctness of the statistics processor in a controlled environment. PASS/
+FAIL is determined by matching the input/output values.
+
+Stage 2 : Load Data
+-------------------
+Submitting network traffic through traffic server with load tools like jtest 
and
+ftest, dumps the statistics to a text file, periodically and examines the
+resulting values
+
+//-----------------------------------------------------------------------------
+// For QA Engineer
+//-----------------------------------------------------------------------------
+The most concerning question for QA engineers is "how can I tell if the 
+Statistics Processor is working correctly?"
+
+Recall, the new Statistics Processor is meant to replace the StatAggregate.cc
+and part of the WebOverview.cc. In essence, you should not see any apparent
+change.
+
+If you ever see a value of -9999.0 (or -9999), then there is an error in
+computing that value.
+
+
+<expr>
+    - %d
+    - %f
+    - %k
+
+<dst>
+    - specifies the variable that stores that result value.
+    - ATTRIBUTE: type
+        built-in: variables that are built-in/defined in traffic server
+        custom: variables that are introducted to be temporary storage or
+                variables that are introdcuted by the client.
+    - default attributes:
+       type = built-in  
+
+<src>
+    - variable need to computer the <dst>
+    - ATTRIBUTE: type
+       node: this is a proxy.node.* variables
+       cluster: the is a proxy.node.* variables but summing over all
+                 nodes in the cluster.
+    - default attributes:
+       type = node
+
+<min>
+    - specifics what is the smallest possible value for <dst>. For values
+      smaller than <min>, the <defualt> is used.
+
+<max>
+    - specifics what is the largest possible value for <dst>. For values
+      larger than <max>, the <defualt> is used.
+
+<default>
+    - specifics what value to be assigned to <dst> is the result <dst>
+      value is smaller then <min> or larger then <max>
+
+RULES: (some of these are enfored by the DTD anyways)
+- all operator and operand in <expr> MUST BE separated by a single space.
+- the order of the tags matters
+- the order of each entry matters
+- each statistics entry has have at most 1 <dst>
+
+
+DEFINED CONSTANT (in alphabetical order)
+* _BYTES_TO_MB_SCALE
+       * Origin: utils/WebMgmtUtils.h
+       * Value:  (1/(1024*1024.0))
+
+*  _HRTIME_SECOND
+       * Origin:
+       * Value: 
+
+*  _MBIT_TO_KBIT_SCALE
+       * Origin: utils/WebMgmtUtils.h
+       * Value:  (1000.0);
+
+*  _PCT_TO_INTPCT_SCALE
+       * Origin: utils/WebMgmtUtils.h
+       * Value:  (100.0);
+
+*  _SECOND_TO_MILLISECOND_SCALE
+       * Origin: utils/WebMgmtUtils.h
+       * Value:  (1000.0);
+
+
+            __DIFFTIME

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 192b967..0915270 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1890,7 +1890,6 @@ AC_CONFIG_FILES([
   mgmt/api/Makefile
   mgmt/api/include/Makefile
   mgmt/cluster/Makefile
-  mgmt/stats/Makefile
   mgmt/utils/Makefile
   mgmt/web2/Makefile
   plugins/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/Makefile.am
----------------------------------------------------------------------
diff --git a/mgmt/Makefile.am b/mgmt/Makefile.am
index 2b69cce..e76f70d 100644
--- a/mgmt/Makefile.am
+++ b/mgmt/Makefile.am
@@ -17,7 +17,7 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-SUBDIRS = cluster utils web2 stats api
+SUBDIRS = cluster utils web2 api
 
 noinst_LIBRARIES = libmgmt_p.a libmgmt_lm.a
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/Makefile.am
----------------------------------------------------------------------
diff --git a/mgmt/stats/Makefile.am b/mgmt/stats/Makefile.am
deleted file mode 100644
index cc6c0c0..0000000
--- a/mgmt/stats/Makefile.am
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Makefile.am for the Enterprise Management module.
-#
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-AM_CPPFLAGS = \
-  -I$(top_srcdir)/lib/records \
-  -I$(top_srcdir)/lib/ts \
-  -I$(top_srcdir)/mgmt \
-  -I$(top_srcdir)/mgmt/cluster \
-  -I$(top_srcdir)/mgmt/utils \
-  -I$(top_srcdir)/mgmt/api/include \
-  -I$(top_srcdir)/mgmt/web2 \
-  -I$(top_srcdir)/proxy \
-  -I$(top_srcdir)/lib \
-  -I$(top_builddir)/lib
-
-noinst_LIBRARIES = libstats.a
-
-libstats_a_SOURCES = \
-  StatProcessor.cc \
-  StatProcessor.h \
-  StatType.cc \
-  StatType.h \
-  StatXML.cc \
-  StatXML.h

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/StatProcessor.cc
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatProcessor.cc b/mgmt/stats/StatProcessor.cc
deleted file mode 100644
index 24cbaa9..0000000
--- a/mgmt/stats/StatProcessor.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-/** @file
-
-  A brief file description
-
-  @section license License
-
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
- */
-
-/***************************************/
-/****************************************************************************
- *
- *  StatProcessor.cc - Functions for computing node and cluster stat
- *                          aggregation
- *
- *
- ****************************************************************************/
-
-#include "ink_config.h"
-#include "StatProcessor.h"
-#include "FileManager.h"
-
-#define STAT_CONFIG_FILE "stats.config.xml"
-
-StatObjectList statObjectList;
-StatXMLTag currentTag = INVALID_TAG;
-StatObject *statObject = NULL;
-char *exprContent = NULL;
-static unsigned statCount = 0;  // global statistics object counter
-bool nodeVar;
-bool sumClusterVar;
-
-// These helpers are used to work around the unsigned char'iness of xmlchar 
when
-// using libxml2. We don't have any tags (now) which uses UTF8.
-static int
-xml_atoi(const xmlchar *nptr)
-{
-  return atoi((const char*)nptr);
-}
-
-static double
-xml_atof(const xmlchar *nptr)
-{
-  return atof((const char*)nptr);
-}
-
-static int
-xml_strcmp(const xmlchar *s1, const char *s2)
-{
-  return strcmp((const char *)s1, s2);
-}
-
-
-static void
-elementStart(void * /* userData ATS_UNUSED */, const xmlchar *name, const 
xmlchar **atts)
-{
-  int i = 0;
-
-  if (!xml_strcmp(name, "ink:statistics"))
-    currentTag = ROOT_TAG;
-  else if (!xml_strcmp(name, "statistics"))
-    currentTag = STAT_TAG;
-  else if (!xml_strcmp(name, "destination"))
-    currentTag = DST_TAG;
-  else if (!xml_strcmp(name, "expression"))
-    currentTag = EXPR_TAG;
-  else
-    currentTag = INVALID_TAG;
-
-  switch (currentTag) {
-  case STAT_TAG:
-    statObject = new StatObject(++statCount);
-    Debug(MODULE_INIT, "\nStat #: ----------------------- %d 
-----------------------\n", statCount);
-
-    if (atts)
-     for (i = 0; atts[i]; i += 2) {
-      ink_assert(atts[i + 1]);    // Attribute comes in pairs, hopefully.
-
-      if (!xml_strcmp(atts[i], "minimum")) {
-        statObject->m_stats_min = (MgmtFloat) xml_atof(atts[i + 1]);
-        statObject->m_has_min = true;
-      } else if (!xml_strcmp(atts[i], "maximum")) {
-        statObject->m_stats_max = (MgmtFloat) xml_atof(atts[i + 1]);
-        statObject->m_has_max = true;
-      } else if (!xml_strcmp(atts[i], "interval")) {
-        statObject->m_update_interval = (ink_hrtime) xml_atoi(atts[i + 1]);
-      } else if (!xml_strcmp(atts[i], "debug")) {
-        statObject->m_debug = (atts[i + 1] && atts[i + 1][0] == '1');
-      }
-
-      Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], 
atts[i + 1]);
-    }
-    break;
-
-  case EXPR_TAG:
-    exprContent = (char*)ats_malloc(BUFSIZ * 10);
-    memset(exprContent, 0, BUFSIZ * 10);
-    break;
-
-  case DST_TAG:
-    nodeVar = true;
-    sumClusterVar = true;       // Should only be used with cluster variable
-
-    if (atts)
-     for (i = 0; atts[i]; i += 2) {
-      ink_assert(atts[i + 1]);    // Attribute comes in pairs, hopefully.
-      if (!xml_strcmp(atts[i], "scope")) {
-        nodeVar = (!xml_strcmp(atts[i + 1], "node") ? true : false);
-      } else if (!xml_strcmp(atts[i], "operation")) {
-        sumClusterVar = (!xml_strcmp(atts[i + 1], "sum") ? true : false);
-      }
-
-      Debug(MODULE_INIT, "\tDESTINTATION w/ attribute: %s -> %s\n", atts[i], 
atts[i + 1]);
-    }
-
-    break;
-
-  case INVALID_TAG:
-    Debug(MODULE_INIT, "==========================================>%s<=\n", 
name);
-    break;
-
-  default:
-    break;
-  }
-}
-
-
-static void
-elementEnd(void * /* userData ATS_UNUSED */, const xmlchar */* name ATS_UNUSED 
*/)
-{
-  switch (currentTag) {
-  case STAT_TAG:
-    statObjectList.enqueue(statObject);
-    currentTag = ROOT_TAG;
-    break;
-
-  case EXPR_TAG:
-    statObject->assignExpr(exprContent); // This hands over ownership of 
exprContent
-    // fall through
-
-  default:
-    currentTag = STAT_TAG;
-    break;
-  }
-}
-
-
-static void
-charDataHandler(void * /* userData ATS_UNUSED */, const xmlchar * name, int /* 
len ATS_UNUSED */)
-{
-  if (currentTag != EXPR_TAG && currentTag != DST_TAG) {
-    return;
-  }
-
-  char content[BUFSIZ * 10];
-  if (XML_extractContent((const char*)name, content, BUFSIZ * 10) == 0) {
-    return;
-  }
-
-  if (currentTag == EXPR_TAG) {
-    ink_strlcat(exprContent, content, BUFSIZ * 10); // see above for the size
-
-  } else {
-    statObject->assignDst(content, nodeVar, sumClusterVar);
-  }
-}
-
-
-StatProcessor::StatProcessor(FileManager * configFiles):m_lmgmt(NULL), 
m_overviewGenerator(NULL)
-{
-  rereadConfig(configFiles);
-}
-
-
-void
-StatProcessor::rereadConfig(FileManager * configFiles)
-{
-  textBuffer *fileContent = NULL;
-  Rollback *fileRB = NULL;
-  char *fileBuffer = NULL;
-  version_t fileVersion;
-  int fileLen;
-
-  statObjectList.clean();
-  statCount = 0;                // reset statistics counter
-
-  int ret = configFiles->getRollbackObj(STAT_CONFIG_FILE, &fileRB);
-  if (!ret) {
-    Debug(MODULE_INIT, " Can't get Rollback for file: %s\n", STAT_CONFIG_FILE);
-  }
-  fileVersion = fileRB->getCurrentVersion();
-  fileRB->getVersion(fileVersion, &fileContent);
-  fileBuffer = fileContent->bufPtr();
-  fileLen = strlen(fileBuffer);
-
-#if HAVE_LIBEXPAT
-  /*
-   * Start the XML Praser -- the package used is EXPAT
-   */
-  XML_Parser parser = XML_ParserCreate(NULL);
-  XML_SetUserData(parser, NULL);
-  XML_SetElementHandler(parser, elementStart, elementEnd);
-  XML_SetCharacterDataHandler(parser, charDataHandler);
-
-  /*
-   * Substiture every newline with a space to get around
-   * the SetCharacterDataHandler problem.
-   */
-  char *newlinePtr;
-  while ((newlinePtr = strchr(fileBuffer, '\n')) != NULL || (newlinePtr = 
strchr(fileBuffer, '\r')) != NULL) {
-    *newlinePtr = ' ';
-  }
-
-  /*
-   * Parse the input file according to XML standard.
-   * Print error if we encounter any
-   */
-  int status = XML_Parse(parser, fileBuffer, fileLen, true);
-  if (!status) {
-    mgmt_log(stderr, "%s at line %d\n", 
XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
-  }
-
-  /*
-   * Cleaning upt
-   */
-  XML_ParserFree(parser);
-#else
-  /* Parse XML with libxml2 */
-  xmlSAXHandler sax;
-  memset(&sax, 0, sizeof(xmlSAXHandler));
-  sax.startElement = elementStart;
-  sax.endElement = elementEnd;
-  sax.characters = charDataHandler;
-  sax.initialized = 1;
-  xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(&sax, NULL, NULL, 0, NULL);
-
-  int status = xmlParseChunk(parser, fileBuffer, fileLen, 1);
-  if (status != 0) {
-    xmlErrorPtr errptr = xmlCtxtGetLastError(parser);
-    mgmt_log(stderr, "%s at %s:%d\n", errptr->message, errptr->file, 
errptr->line);
-  }
-  xmlFreeParserCtxt(parser);
-#endif
-
-
-  delete fileContent;
-
-  Debug(MODULE_INIT, "\n\n---------- END OF PARSING & INITIALIZING 
---------\n\n");
-}
-
-
-StatProcessor::~StatProcessor()
-{
-
-  Debug(MODULE_INIT, "[StatProcessor] Destructing Statistics Processor\n");
-
-}
-
-
-void
-setTest()
-{
-  char var_name[64];
-
-  for (int i = 1; i <= 5; i++) {
-    memset(var_name, 0, 64);
-    snprintf(var_name, sizeof(var_name), "proxy.node.stats.test%d", i);
-    if (i == 4) {
-      MgmtFloat tmp;
-      varFloatFromName("proxy.node.stats.test4", &tmp);
-      varSetFloat(var_name, tmp + 1, true);
-    } else {
-      varSetFloat(var_name, i, true);
-    }
-  }
-}
-
-
-void
-verifyTest()
-{
-  MgmtFloat tmp1, tmp2;
-
-  // 1. simple copy
-  varFloatFromName("proxy.node.stats.test1", &tmp1);
-  varFloatFromName("proxy.node.stats.test2", &tmp2);
-  if (tmp1 == tmp2) {
-    Debug(MODULE_INIT, "PASS -- simple copy");
-  } else {
-    Debug(MODULE_INIT, "FAIL -- simple copy");
-  }
-
-  // 2. simple interval
-  varFloatFromName("proxy.node.stats.test3", &tmp2);
-  if (tmp2 >= 10) {
-    Debug(MODULE_INIT, "PASS -- simple interval & constant");
-  } else {
-    Debug(MODULE_INIT, "FAIL -- simple interval & constant %f", tmp2);
-  }
-
-  // 3. delta
-  varFloatFromName("proxy.node.stats.test4", &tmp2);
-  if ((tmp2 > 150) && (tmp2 < 250)) {
-    Debug(MODULE_INIT, "PASS -- delta");
-  } else {
-    Debug(MODULE_INIT, "FAIL -- delta %f", tmp2);
-  }
-}
-
-
-/**
- * Updating the statistics NOW.
- **/
-unsigned short
-StatProcessor::processStat()
-{
-  unsigned short result = 0;
-
-  Debug(MODULE_INIT, "[StatProcessor] Processing Statistics....\n");
-
-//    setTest();
-  statObjectList.Eval();
-//    verifyTest();
-
-  return (result);
-}
-
-
-/**
- * ExpressionEval
- * --------------
- *
- */
-RecData
-ExpressionEval(char *exprString)
-{
-  RecDataT result_type;
-  StatObject statObject;
-
-  char content[BUFSIZ * 10];
-  XML_extractContent(exprString, content, BUFSIZ * 10);
-
-  statObject.assignExpr(content);
-  return statObject.NodeStatEval(&result_type, false);
-}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9e6d233f/mgmt/stats/StatProcessor.h
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatProcessor.h b/mgmt/stats/StatProcessor.h
deleted file mode 100644
index 71dab2f..0000000
--- a/mgmt/stats/StatProcessor.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/** @file
-
-  A brief file description
-
-  @section license License
-
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
- */
-
-#ifndef _STAT_PROCESSOR_H_
-#define _STAT_PROCESSOR_H_
-
-/****************************************************************************
- *
- *  StatProcessor.h - Functions for computing node and cluster stat
- *                          aggregation
- *
- *
- ****************************************************************************/
-
-#include "ink_platform.h"
-#include <stdarg.h>
-#include "MgmtUtils.h"
-#include "MgmtDefs.h"
-#include "WebMgmtUtils.h"
-#include "ink_hrtime.h"
-#include "LocalManager.h"
-#include "WebOverview.h"
-
-#define _HEADER
-#define _D(x)
-#define _FOOTER
-#include "DynamicStats.h"
-#include "StatType.h"
-
-#if HAVE_LIBEXPAT
-#include "expat.h"
-typedef XML_Char xmlchar;
-#elif HAVE_LIBXML2
-#include <libxml/parser.h>
-#include <libxml/SAX.h>
-typedef xmlChar xmlchar;
-#else
-# error "No XML parser - please configure expat or libxml2"
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-
-class StatProcessor
-{
-public:
-
-  explicit StatProcessor(FileManager * configFiles);
-  ~StatProcessor();
-
-  // Member Fuctions
-  unsigned short processStat();
-  void rereadConfig(FileManager * configFiles);
-
-  LocalManager *m_lmgmt;
-  overviewPage *m_overviewGenerator;
-};
-
-
-/**
- * External expression evaluation API.
- *
- * INPUT: an expression string, e.g.:
- * "(proxy.node.user_agent_total_bytes-proxy.node.origin_server_total_bytes)
- *  / proxy.node.user_agent_total_bytes"
- *
- * RETURN: the resulting value of the expression.
- * NOTE: it returns -9999.0 if there is an error.
- *
- */
-
-RecData ExpressionEval(char *);
-
-#endif

Reply via email to