I want to monitor + export in/out traffic data to XML with MRTG and rrdtool xport. I have several problems:
The exported XML has the same timestamp for start and end in the meta section. I specified --start 1429862400 --end 1429894800, the output values start at 1429862700 and end at 1429886100, also I'm getting quite a few NaNs.
I mapped ds0 and ds1 to my in/out variables, but I'm not actually sure where to define ds's in the first place. How can I map my variables to network in and out traffic? Where are the ds-devices configured?
Ds1, probably because not properly configured, produces faulty values.
I'm running
rrdtool xport\
DEF:out_bytes=localhost_2.rrd:ds0:AVERAGEDEF:in_bytes\
=localhost_2.rrd:ds1:AVERAGE CDEF:io_bytes=out_bytes,in_bytes,+\
XPORT:in_bytes:outbytes XPORT:out_bytes:inbytes XPORT:io_bytes:iobytes\
--enumds --start 1429862400 --end 1429894800
to export.
This is my mrtg.cfg
WorkDir: /var/www/mrtg/graph
WriteExpires: Yes
Title[^]: Traffic Analysis for
EnableIPv6: no
Target[localhost_2]: 2:public#127.0.0.1:
SetEnv[localhost_2]: MRTG_INT_IP="No Ip" MRTG_INT_DESCR="eth0"
MaxBytes[localhost_2]: 1250000
Title[localhost_2]: Traffic Analysis for 2 -- SMDSP01
XSize[localhost_2]: 256
YSize[localhost_2]: 64
XScale[localhost_2]: 0.65
YScale[localhost_2]: 0.6
Unscaled[localhost_2]: d
WithPeak[localhost_2]: d
Here's a snipped of the output
<?xml version="1.0" encoding="UTF-8"?> <xport> <meta>
<start>1429862700</start>
<step>300</step>
<end>1429862700</end>
<rows>109</rows>
<columns>3</columns>
<legend>
<entry>outbytes</entry>
<entry>inbytes</entry>
<entry>iobytes</entry>
</legend> </meta> <data>
<row>
<t>1429862700</t>
<v0>7.5489722222e+00</v0>
<v1>1.4522986944e+05</v1>
<v2>1.4523741842e+05</v2>
</row>
<row>
<t>1429863000</t>
<v0>9.3254770432e+00</v0>
<v1>1.6219456095e+05</v1>
<v2>1.6220388643e+05</v2>
</row>
<row>
<t>1429863300</t>
<v0>6.4311896235e+00</v0>
<v1>1.6358109508e+05</v1>
<v2>1.6358752627e+05</v2>
</row>
<row>
<t>1429863600</t>
<v0>9.8945000000e+00</v0>
<v1>4.6888782408e+05</v1>
<v2>4.6889771858e+05</v2>
</row>
<row>
<t>1429863900</t>
<v0>5.6088333333e+00</v0>
<v1>4.2072387378e+05</v1>
<v2>4.2072948261e+05</v2>
</row>
<row>
<t>1429864200</t>
<v0>2.0383366480e+01</v0>
<v1>2.5505514117e+05</v1>
<v2>2.5507552453e+05</v2>
</row>
<row>
<t>1429864500</t>
<v0>1.2132332724e+03</v0>
<v1>2.1026807079e+06</v1>
<v2>2.1038939412e+06</v2>
</row>
<row>
<t>1429864800</t>
<v0>2.3604750000e+01</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429865100</t>
<v0>6.3642958611e+03</v0>
<v1>1.1198971143e+07</v1>
<v2>1.1205335438e+07</v2>
</row>
<row>
<t>1429865400</t>
<v0>1.5586544194e+04</v0>
<v1>8.5607161284e+06</v1>
<v2>8.5763026726e+06</v2>
</row>
<row>
<t>1429865700</t>
<v0>2.4014277778e+01</v0>
<v1>3.3303833329e+06</v1>
<v2>3.3304073472e+06</v2>
</row>
...
<row>
<t>1429892100</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429892400</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429892700</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429893000</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429893300</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429893600</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429893900</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429894200</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429894500</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429894800</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row>
<row>
<t>1429895100</t>
<v0>NaN</v0>
<v1>NaN</v1>
<v2>NaN</v2>
</row> </data> </xport>
Thanks for your help!
Firstly, the invalid <end> tag in the XML output is a bug in RRDTool. You do not say which version you are using, but if you are not using the latest, please upgrade. If you ARE using the latest, please report a bug :)
The output time points are slightly off from your requested window because of fenceposting. You're specifying the data points, and are exporting the RRA containing them (which will end 1 step later). This is a bit counterintuitive but I think it is by design.
You are defining your variable DEFs from your RRD DSs thus:
DEF:out_bytes=localhost_2.rrd:ds0:AVERAGE
DEF:in_bytes=localhost_2.rrd:ds1:AVERAGE
An RRD file generated by MRTG will always have exactly two DSs -- called ds0 and ds1. Although RRDTool can support many more DSs with all sorts of names, you cannot change the names in an MRTG-generated RRD file, not can you add or remove a DS, without breaking MRTG. If you want to have more DSs, the only way to do it is to add a new MRTG Target -- which will create a new RRD file, with DSs 'ds0' and 'ds1' -- and then add this to your Xport request as an addiitonal two DEF lines.
The NaNs are where the underlying RRA does not have valid data. This is likely because there simply has not been (enough) data collected for that time window, or the collected data were invalid. The corresponding MRTG graphs will likely show nothing as well. Another possibility is that the wrong RRA is being selected, but this is unlikely as your time window is only 9hr which fits fine into the default 1-day high-granularity RRA generated by MRTG.
If your values are faulty, then verify that they are not faulty in the RRD already - xport only outputs what is in the database. Are you expecting an output in Bits and not Bytes (in which case multiply by 8)? Are you values around 140Mbps (IE 18MBps) but you are querying via SNMPv1 in which case MRTG is unable to poll the data? In this case, use SNMPv2 with MRTG to fetch the correct data. Unfortunately you have not given any details how the data are 'faulty' so I can only speculate.
Related
I have a quick question to my XSLT 1.0 problem.
My payload looks like this
<Document_Lines>
<row>
<ItemCode>153231105</ItemCode>
<Quantity>24.000</Quantity>
<ShipDate>22.12.2021 00:00:00</ShipDate>
<Charge>0311232021</Charge>
<MhdDate>02.11.2026 00:00:00</MhdDate>
<UomCode>ST</UomCode>
<BaseType>17</BaseType>
<BaseEntry>10</BaseEntry>
<BaseLine>001</BaseLine>
</row>
<row>
<ItemCode>153105</ItemCode>
<Quantity>12.000</Quantity>
<ShipDate>22.12.2021 00:00:00</ShipDate>
<Charge>03112320212</Charge>
<MhdDate>12.05.2026 00:00:00</MhdDate>
<UomCode>ST</UomCode>
<BaseType>17</BaseType>
<BaseEntry>10</BaseEntry>
<BaseLine>001</BaseLine>
</row>
<row>
<ItemCode>153466101</ItemCode>
<Quantity>36.000</Quantity>
<ShipDate>22.12.2021 00:00:00</ShipDate>
<Charge>0212123321</Charge>
<MhdDate>01.12.2026 00:00:00</MhdDate>
<UomCode>ST</UomCode>
<BaseType>17</BaseType>
<BaseEntry>10</BaseEntry>
<BaseLine>003</BaseLine>
</row>
</Document_Lines>
and I need to get an output like this
<row>
<Charge>0311232021</Charge>
<Quantity>25</Charge>
<LineNum>0</LineNum>
</row>
<row>
<Charge>0311232021</Charge>
<Quantity>12</Charge>
<LineNum>0</LineNum>
</row>
<row>
<Charge>0212123321</Charge>
<Quantity>36</Charge>
<LineNum>1</LineNum>
</row>
So it should only count the LineNum when the ItemCode before is unequal to the Itemcode.
My solution was something like:
count(//iwss2:row[not(preceding-sibling::iwss2:row/iwss2:ItemCode = $itemCode)])
but with this, the LineNum will be
0,0,2 instead of 0,0,1
My Question: How can I apply double (or multiple) grouping?
Here is the source XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<row>
<Type>1</Type>
<WeaNr>100519</WeaNr>
</row>
<row>
<Type>2</Type>
<WeaNr>100519</WeaNr>
<ETADC_SKU>2007925</ETADC_SKU>
<CrossDock>N</CrossDock>
</row>
<row>
<Type>2</Type>
<WeaNr>100519</WeaNr>
<ETADC_SKU>12007925</ETADC_SKU>
<CrossDock>N</CrossDock>
</row>
<row>
<Type>2</Type>
<WeaNr>100519</WeaNr>
<ETADC_SKU>200792ww5</ETADC_SKU>
<CrossDock>Y</CrossDock>
</row>
<row>
<Type>1</Type>
<WeaNr>100520</WeaNr>
</row>
<row>
<Type>2</Type>
<WeaNr>100520</WeaNr>
<ETADC_SKU>2007925444</ETADC_SKU>
<CrossDock>N</CrossDock>
</row>
<row>
<Type>2</Type>
<WeaNr>100520</WeaNr>
<ETADC_SKU>2007925333</ETADC_SKU>
<CrossDock>Y</CrossDock>
</row>
<row>
<Type>2</Type>
<WeaNr>100520</WeaNr>
<ETADC_SKU>204445333</ETADC_SKU>
<CrossDock>Y</CrossDock>
</row>
</root>
I want use grouping by WeaNr and CrossDock
Expected results in this case are 4 groups:
1. WeaNr=100519 and CrossDock=N
2. WeaNr=100519 and CrossDock=Y
3. WeaNr=100520 and CrossDock=N
4. WeaNr=100520 and CrossDock=Y
Grouping just by one field, like WeaNr is easy:
<xsl:for-each-group select="row" group-by="WeaNr">
So how can I apply double (or multiple) grouping?
You would group-by some string that is a combination of the two, for example
<xsl:for-each-group select="row" group-by="concat(WeaNr, '|', CrossDock)">
or alternatively use two nested levels of for-each-group
<xsl:for-each-group select="row" group-by="WeaNr">
<xsl:for-each-group select="current-group()" group-by="CrossDock">
The difference between these two approaches is apparent if you use the position() function in the body of the for-each-group - in the concat case you'll get position values from 1 to 4, in the nested case you'll get 1, 2, 1, 2 (because the position() is determined by the nearest enclosing for-each-group). Similarly, last() will be 4 in the concat case and 2 in the nested case.
I am trying to write XSLT file for following input XML to output XML, is it possible XSLT to convert the value of input xml as node in output XML? how can I implement this?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<Rows>
<Row><xml_data_name/> <xml_data_value/> </Row>
<Row><xml_data_name>persons</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>username</xml_data_name> <xml_data_value>JS1</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value>John</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value>Smith</xml_data_value> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>username</xml_data_name> <xml_data_value>MI1</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value>Morka</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value>Ismincius</xml_data_value> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>persons</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name/> <xml_data_value/> </Row>
</Rows>
Output XML
<?xml version="1.0" ?>
<persons>
<person username="JS1">
<name>John</name>
<family-name>Smith</family-name>
</person>
<person username="MI1">
<name>Morka</name>
<family-name>Ismincius</family-name>
</person>
</persons>
You could certainly use xsl:element like
<xsl:template match="Row">
<!-- Note {} brackets in name attribute -->
<xsl:element name="{xml_data_name}">
<xsl:value-of select="xml_data_value" />
</xsl:element>
</xsl:template>
What would be greater problem is a structure of output because it is not easy to decide which rows should be nested, which rows should transform into an attribute rather than element etc.
Well, that's one of the weirdest data formats I've ever seen! Are you sure you can't get whatever produced this to produce something more reasonable?
I think the solution has to be recursion: you want a function that takes a sequence of rows as input; it outputs an element whose name is the name of the first element in the sequence with no data value and whose content is obtained by a recursive call that passes all rows after that first row up to the next row with no data value and the same name, then calls itself to process all rows after that row. Not easy, and certainly takes more time than I allow myself for answering SO questions!
I have been banging my head against a wall on this for a while. Our application processes a complex structure XML invoice from another system. The invoice contains rows of information which contain various counts. These counts may or may not contain a value. There is an overall document charge. We need to work out the unit charge. The formula would be the total cost divided by the total of the counts.
I have been working through the examples kindly provided by others regarding summing in XSLT1.0. I can use xsl:call-template to get the sum of the counts, but I don't know how to apply the result to the calculate the unit price.
Sample XML
<Document>
<Row>
<Count1>
<Value>10</Value>
</Count1>
<Count2>
<Value/>
</Count2>
</Row>
<Row>
<Count1>
<Value>5</Value>
</Count1>
<Count2>
<Value>6</Value>
</Count2>
</Row>
<Row>
<Count1>
<Value>2</Value>
</Count1>
<Count2>
<Value>3</Value>
</Count2>
</Row>
<Charge>
<Value>260</Value>
</Charge>
</Document>
If I could see how to get the following XML output that would probably show me what I need.
<Document>
<Row>
<Total>10</Total>
<UnitPrice>10</UnitPrice>
</Row>
<Row>
<Total>11</Total>
<UnitPrice>10</UnitPrice>
</Row>
<Row>
<Total>15</Total>
<UnitPrice>10</UnitPrice>
</Row>
</Document>
Many thanks in advance
You just need to call sum() on the required Values, like so:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/Document">
<Document>
<xsl:apply-templates select="Row"/>
</Document>
</xsl:template>
<xsl:template match="Row">
<Row>
<Total>
<xsl:value-of select="sum(./*[Value>0]/Value)"/>
</Total>
<UnitPrice>10</UnitPrice>
</Row>
</xsl:template>
</xsl:stylesheet>
This gives the output:
<Document>
<Row>
<Total>10</Total>
<UnitPrice>10</UnitPrice>
</Row>
<Row>
<Total>11</Total>
<UnitPrice>10</UnitPrice>
</Row>
<Row>
<Total>5</Total>
<UnitPrice>10</UnitPrice>
</Row>
</Document>
I need to transform an XML file by adding a new element that will have a value based on data in the current file and two other XML files using XSLT 1.0. The files:
File1:
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
</Row>
</Table>
File2:
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue1</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue1</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue2</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue3</ColC>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
<ColC>CValue1</ColC>
</Row>
</Table>
File3:
<Table>
<Row>
<ColC>CValue1</ColC>
<ColD>ABC</ColD>
</Row>
<Row>
<ColC>CValue2</ColC>
<ColD>DEF</ColD>
</Row>
<Row>
<ColC>CValue3</ColC>
<ColD>DEF</ColD>
</Row>
</Table>
Rows in File1 have a one-to-many relationship with rows in File2 by ColA and ColB.
Rows in File2 have a many-to-one relationship with rows in File3 by ColC.
For each row in File1, I need to:
Look up distinct ColC values in File2 for rows matching on ColA and ColB
For each distinct ColC value, look up ColD value in File3 for rows matching on ColC
Count the number of occurences of ColD values looked up in step 2. ColD will have one of two values (say "ABC" or "DEF"). I need to know if there are more "ABC" than "DEF" and if so add <ColD>ABC</ColD> to that Row in File1. Otherwise add <ColD>DEF</ColD> to that Row in File1. At the end, each Row in File1 should have <ColD>ABC</ColD> or <ColD>DEF</ColD>.
Desired Result (File1 transformed):
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColD>DEF</ColD>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
<ColD>ABC</ColD>
</Row>
</Table>
<ColD>DEF</ColD> would be added to the first Row since there were two occurences of DEF compared to 1 (distinct) occurence of ABC. <ColD>ABC</ColD> would be added to Row 2 since there was one occurence of ABC and zero DEF.
Well, I'll try a suggestion but I'm not sure to have understand everything well, so I'll delete it if it makes no sense.
I understand that you got some related Xml files model. I mean you need to reach elements in files 2 related to elements in File1 and so on.
I got some similar case and fix it by applying a first transform to solve all the references in all files first (duplicating XML nodes everywhere) and applying my more specifics transform on this 'un-related model'. It's way easier but may have some performances issues (un-relate all files could be long if lots of relations), in my case it was for off-line generation so no good perf needed.
I came up with a solution that appears to work (haven't tested it very much). I'm retyping it here so beware of typos. The thing I had the hardest time with was figuring out how to select unique ColC values. Looking at the solution again I didn't need unique values. I had it in my mind that I would loop thru $unique_c_values to count ColD values, but it turned out differently.
<xsl:template match="node()|#">
<xsl:copy>
<xsl:apply-templates select="node()|#">
</xsl:copy>
</xsl:template>
<xsl:template match="Row">
<xsl:copy>
<xsl:apply-templates select="node()|#">
<xsl:variable name="unique_c_values" select=
"document('file2.xml')/Table/Row[current()/ColA = ColA and current()/ColB = ColB]/ColC[not(. = preceding-sibling::*/ColC[current()/ColA = ../ColA and current()/ColB = ../ColB])]"/>
<xsl:variable name="d_values" select=
"document('file3.xml')/Table/Row[ColC = $unique_c_values]/ColD"/>
<xsl:choose>
<xsl:when test="count($d_values[. = 'DEF']) > count($d_values[. = 'ABC'])">
<ColD>DEF</ColD>
</xsl:when>
<xsl:otherwise>
<ColD>ABC</ColD>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>