***********************
** FILE NAME : message.cpp
** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring
-** VERSION NUMBER : $Revision: 1.16 $
+** VERSION NUMBER : $Revision: 1.19 $
**
** DESCRIPTION : Implementation of the message handling classes
**
********************************
** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
** CREATION DATE : Sep 16, 2002
-** LAST UPDATE : Nov 28, 2003
+** LAST UPDATE : Nov 02, 2007
** MODIFICATIONS :
**************************************************************************/
/*****************************
$Log: message.cpp,v $
- Revision 1.16 2003-12-04 10:38:09 arjen
+ Revision 1.19 2011-03-24 10:20:37 arjen
+ Added some debug info
+
+ Revision 1.18 2007/11/03 10:23:53 arjen
+ Handling of parameters is greatly improved.
+ When creating a new parameter from an XML report which is fed into
+ gcm_input, the class definition is used as a template to fill in
+ the default values for the properties.
+ The class is also used as a template when a new property is added
+ to an existing parameter.
+
+ DYNAMIC properties are now handled properly. Instead of making a
+ 'changed property' notification, the value is checked against the
+ defined range for the property. An 'out of range' notification
+ is created when this condition is detected.
+
+ Revision 1.17 2005/05/31 05:51:41 arjen
+ Textual changes in parameter notifications
+
+ Revision 1.16 2003/12/04 10:38:09 arjen
Major redesign. All input is handled through XML. Raw input data is first
transformed into an XML document for further processing.
A collection of polymorphic classes handle the transformation of various
*****************************/
-static const char *RCSID = "$Id: message.cpp,v 1.16 2003-12-04 10:38:09 arjen Exp $";
+#include <unistd.h>
+#include <fstream>
#include <algorithm>
#include <libxml/xpath.h>
#include <libxml/debugXML.h>
+
#include "message.h"
//#define DEBUG
double uncertainty;
+#ifdef DEBUG
+ *Log << "Checking for a mail header.\n";
+#endif
+
/* First, check if the message has a mail header. */
if (input >> line && line == re_uxmail_from)
* out what the content of the message is.
*/
+#ifdef DEBUG
+ *Log << "Classifying message.\n";
+#endif
+
uncertainty = 1.0;
- while (input >> line && uncertainty > 0.1)
+ while (input >> line && uncertainty > epsilon)
{
if (verbose)
{
classification = COOKER_OBJECT;
}
}
+
input.rewind();
certainty = 1.0 - uncertainty;
** VARS CHANGED :
** FUNCTIONS USED :
** SEE ALSO :
-** LAST MODIFIED : Nov 28, 2003
+** LAST MODIFIED : Nov 02, 2007
**=========================================================================
*/
{
String name;
String value;
+ String type;
+ double minimum;
+ double maximum;
};
void client_message::enterXML()
}
if (strcmp((char *)node->name, "log") == 0)
{
+ int log_entry_counter = 0;
+
// Each child contains a log entry, raw or cooked.
node = node->children;
{
if (node->type == XML_ELEMENT_NODE)
{
+ log_entry_counter++;
+
xmlNodePtr item;
String log_hostname;
UTC log_date;
String raw("");;
String log_service;
+ if (verbose)
+ {
+ *Log << "Log entry " << log_entry_counter << " is " << node->name << "\n";
+ }
if (strcmp((char *)node->name, "raw") == 0 && node->children != NULL)
{
item = node->children;
}
log_service = pan.lc->service();
log_date = pan.lc->timestamp();
+
+ if (!log_date.proper())
+ {
+ *Log << log_date << " is not a valid timestamp.\n";
+ }
}
else
{
}
else
{
- *Log << "<timestamp> missing from cooked log element.\n";
+ *Log << "<timestamp> missing from cooked log element nr " << log_entry_counter << ".\n";
}
res = xmlXPathEval((const xmlChar *)"raw/text()", pathcontext);
}
else
{
- *Log << "<raw> missing from cooked log element.\n";
+ *Log << "<raw> missing from cooked log element nr " << log_entry_counter << ".\n";
}
}
}
}
+ else
+ {
+ *Log << "Can not insert log element " << raw << ".\n";
+ }
}
node = node->next;
String qry;
String insertion;
String change_notification("");
+ String out_of_range_notification("");
String create_notification("");
String remove_notification("");
bool initial_entry = false;
String param_class((const char *)xmlGetProp(node, (const xmlChar *)"class"));
+ std::list<param_property> parameter_template;
+ int nr_properties;
+
+ // Obtain a list of properties from the parameter's class.
+ // This list is used to create new parameters.
+
+ qry = "select * from parameter_class where name='";
+ qry += param_class + "'";
+ nr_properties = database.Query(qry);
+ for (int i = 0; i < nr_properties; i++)
+ {
+ param_property pp;
+
+ pp.name = database.Field(i, "property_name");
+ pp.type = database.Field(i, "property_type");
+ pp.minimum = database.Field(i, "min");
+ pp.maximum = database.Field(i, "max");
+
+ parameter_template.push_back(pp);
+ }
+
+#ifdef DEBUG
+ *Log << "Entering a list of " << param_class << " parameters.\n";
+#endif
pathcontext->node = node;
- // If we don;t have any parameters of this class, this will be
+ // If we don't have any parameters of this class, this will be
// an initial entry.
qry = "select name from parameter where objectid='";
{
String param_name((const char *)xmlGetProp(node, (const xmlChar *)"name"));
+#ifdef DEBUG
+ *Log << "Parameter with name " << param_name << "\n";
+#endif
std::list<param_property> properties;
param_property prop;
xmlNodePtr item;
// The parameter exists in the database; check all properties.
bool param_changed = false;
+ bool out_of_range = false;
paramid = database.Field(0, "paramid");
while (pi != properties.end())
{
- qry = "select value from property where paramid='";
+ qry = "select * from property where paramid='";
qry += paramid + "' and name='";
qry += pi->name + "'";
if (database.Query(qry) == 0)
{
*Log << "Property " << pi->name << " of "
<< param_name << " does not exist.\n";
- }
- else if (database.Field(0, "value") != pi->value)
- {
- *Log << "Property " << pi->name << " of "
- << param_name << " is different.\n";
+ // Find the property in the template from the class.
- insertion = "update property set value='";
- insertion += pi->value + "' where paramid='";
- insertion += paramid + "' and name='";
- insertion += pi->name + "'";
+ String property_type("STATIC");
+ double property_minimum = 0.0;
+ double property_maximum = 0.0;
+ std::list<param_property>::iterator ti = parameter_template.begin();
+ while (ti != parameter_template.end() && ti->name != pi->name)
+ {
+ ti++;
+ }
+ if (ti != parameter_template.end())
+ {
+ property_type = ti->type;
+ property_minimum = ti->minimum;
+ property_maximum = ti->maximum;
+ }
+
+ insertion = "insert into property (paramid, name, value, type, min, max) values ('";
+ insertion += paramid + "', '";
+ insertion += pi->name + "', '";
+ insertion += pi->value + "', '";
+ insertion += property_type + "', '";
+ insertion += String(property_minimum) + "', '";
+ insertion += String(property_maximum) + "')";
database.Query(insertion);
insertion = "insert into history (paramid, modified,";
insertion += " change_nature, changed_property, new_value)";
insertion += " values ('";
insertion += paramid + "', '" + arrival.format("%Y-%m-%d %T")
- + "', 'MODIFIED', '";
+ + "', 'CREATED', '";
insertion += pi->name + "', '";
insertion += pi->value + "')";
-
database.Query(insertion);
+ }
+ else
+ {
+ param_property stored_property;
- param_changed = true;
+ stored_property.value = database.Field(0, "value");
+ stored_property.type = database.Field(0, "type");
+ stored_property.minimum = database.Field(0, "min");
+ stored_property.maximum = database.Field(0, "max");
+ if (stored_property.value != pi->value)
+ {
+ *Log << "Property " << pi->name << " of "
+ << param_name << " is different.\n";
+
+ insertion = "update property set value='";
+ insertion += pi->value + "' where paramid='";
+ insertion += paramid + "' and name='";
+ insertion += pi->name + "'";
+
+ database.Query(insertion);
+ insertion = "insert into history (paramid, modified,";
+ insertion += " change_nature, changed_property, new_value)";
+ insertion += " values ('";
+ insertion += paramid + "', '" + arrival.format("%Y-%m-%d %T")
+ + "', 'MODIFIED', '";
+ insertion += pi->name + "', '";
+ insertion += pi->value + "')";
+
+ database.Query(insertion);
+ if (stored_property.type == "DYNAMIC")
+ {
+ // Check the value against the range of the property.
+
+ double numeric_value = pi->value;
+ if (numeric_value < stored_property.minimum || numeric_value > stored_property.maximum)
+ {
+ out_of_range = true;
+ }
+ }
+ else
+ {
+ // A STATIC property changed.
+ param_changed = true;
+ }
+
+ }
}
pi++;
}
{
if (change_notification == "")
{
- remark = "Gnucomo detected a different version for package parameter(s) ";
+ remark = "Gnucomo detected a different property for parameter(s) ";
change_notification = database.new_notification(objectid,
"property modified", remark);
}
*Log << "gcm_input ERROR: Cannot create 'property modified' notification.\n";
}
}
+
+ if (out_of_range)
+ {
+ if (out_of_range_notification == "")
+ {
+ remark = "Gnucomo detected that a property value is out of range.";
+ out_of_range_notification = database.new_notification(objectid,
+ "property out of range", remark);
+ }
+
+ if (out_of_range_notification != "")
+ {
+ qry = "select * from parameter_notification where notificationid='";
+ qry += out_of_range_notification + "' and paramid='";
+ qry += paramid + "'";
+
+ if (database.Query(qry) == 0)
+ {
+ insertion = "insert into parameter_notification";
+ insertion += " (notificationid, paramid) values ('";
+ insertion += out_of_range_notification + "', '";
+ insertion += paramid + "')";
+
+ database.Query(insertion);
+ }
+ }
+ else
+ {
+ *Log << "gcm_input ERROR: Cannot create 'property out of range' notification.\n";
+ }
+ }
}
else
{
while (pi != properties.end())
{
- insertion = "insert into property (paramid, name, value, type) values ('";
+ // Find the property in the template from the class.
+
+ String property_type("STATIC");
+ double property_minimum = 0.0;
+ double property_maximum = 0.0;
+ std::list<param_property>::iterator ti = parameter_template.begin();
+
+ while (ti != parameter_template.end() && ti->name != pi->name)
+ {
+ ti++;
+ }
+ if (ti != parameter_template.end())
+ {
+ property_type = ti->type;
+ property_minimum = ti->minimum;
+ property_maximum = ti->maximum;
+ }
+
+ insertion = "insert into property (paramid, name, value, type, min, max) values ('";
insertion += paramid + "', '";
insertion += pi->name + "', '";
- insertion += pi->value + "', 'STATIC')";
+ insertion += pi->value + "', '";
+ insertion += property_type + "', '";
+ insertion += String(property_minimum) + "', '";
+ insertion += String(property_maximum) + "')";
database.Query(insertion);
insertion = "insert into history (paramid, modified,";
{
if (create_notification == "")
{
- remark = "Gnucomo detected new parameter(s) of class package";
+ remark = "Gnucomo detected new parameter(s) of class " + param_class;
create_notification = database.new_notification(objectid,
"parameter created", remark);
}
{
// Check if any parameters in this class have disappeared.
+#ifdef DEBUG
+ *Log << "Checking for disappeared parameters.\n";
+#endif
qry = "select name, paramid from parameter where objectid='";
qry += objectid + "' and class='" + param_class + "'";
int nr_parameters = database.Query(qry);
- pqxx::Result parameter_set = database.Result();
+ pqxx::result parameter_set = database.Result();
+#ifdef DEBUG
+ *Log << nr_parameters << " parameters of class " << param_class << " found in the database.\n";
+#endif
for (int i = 0; i < nr_parameters; i++)
{
String XPath;
String param_name, paramid;
param_name = database.Field(parameter_set, i, "name");
+#ifdef DEBUG
+ *Log << "Looking for " << param_name << " in XML tree.\n";
+#endif
XPath = "gcmt:parameter[@name='" + param_name + "']";
res = xmlXPathEval((const xmlChar *)(const char *)XPath, pathcontext);
- if (res->nodesetval->nodeTab == NULL)
+#ifdef DEBUG
+ *Log << "XPATH result: " << res->type << ".\n";
+ *Log << "Nr of nodes found: " << res->nodesetval->nodeNr << ".\n";
+#endif
+ if (res->nodesetval->nodeNr == 0)
{
// The parameter is in the database but not in the report
+#ifdef DEBUG
+ *Log << "Could not find " << XPath << " in XML tree.\n";
+#endif
paramid = database.Field(parameter_set, i, "paramid");
qry ="select change_nature from history where paramid='";
qry += paramid + "' order by modified desc";
if (remove_notification == "")
{
- remark = "Gnucomo detected that package(s) have disappeared ";
+ remark = "Gnucomo detected that " + param_class
+ + " parameters(s) have disappeared ";
remove_notification = database.new_notification(objectid,
"parameter removed", remark);
}
}
}
}
+ else
+ {
+#ifdef DEBUG
+ *Log << XPath << " was found in XML tree.\n";
+#endif
+ }
}
}
}
}
}
+void client_message::saveXML(String filename)
+{
+ std::ofstream xmlfile;
+
+ xmlfile.open(filename);
+ xmlfile << xmlBuffer.str();
+}
+
/*=========================================================================
** NAME : enter
** SYNOPSIS : int enter()
** PARAMETERS :
** RETURN VALUE : The number of lines successfully parsed from the input
+** or a negative number if an error is encountered.
+** The error return values are:
+** -1 No database connection.
+** -2 XML parser error.
**
** DESCRIPTION :
**
** VARS CHANGED :
** FUNCTIONS USED :
** SEE ALSO :
-** LAST MODIFIED : Nov 26, 2003
+** LAST MODIFIED : Sep 22, 2020
**=========================================================================
*/
int client_message::enter()
{
+ int nr_lines = 0;
+
pan.mf->set_message_type(pan.lc->message_type());
pan.mf->construct_XML(input, xmlBuffer);
xmlDom = xmlParseMemory(xmlBuffer.str(), xmlBuffer.pcount());
- if (xmlDom)
+ if (database.is_connected())
{
- if (extractHeader())
+ if (xmlDom)
+ {
+ if (extractHeader())
+ {
+ enterXML();
+ }
+ }
+ else
{
- enterXML();
+ *Log << "XML parser FAILED.\n";
+ nr_lines = -2; // XML parse error
}
}
else
{
- *Log << "XML parser FAILED.\n";
+ *Log << "Database connection FAILED.\n";
+ nr_lines = -1; // No database connection
}
- return 0;
+ return nr_lines;
}