vthttpd -dump et requêtes SQL - tlist ameliore - vtexport.xml to csv

<Article mis à jour> J’ai rajouté pas mal de petites astuces sur cet article qui date de l’année dernière (filtre XSL - merci Joey pour l’inspiration !, parsing java, utilisation de Panda Python)

Une autre astuce avec le Webaccess VTOM vthttpd : on peut dump tout le contenu de la base VTOM en un fichier exploitable par SQLITE !

L’utilisation principale est de lister tous les jobs, à la manière d’un tlistmais avec beaucoup plus d’informations (dont les paramètres et le script)

Voici un petit exemple :

vthttpd -dump /var/tmp/vthttpd.dat
sqlite3 /var/tmp/vthttpd.dat
>
.tables  -- liste toutes les tables
pragma table_info(JOBS) ; -- liste les champs de la table JOBS
.schema JOBS ; -- idem mais avec le type de la colonne (varchar(35) par ex.)

On peut exécuter un script .sql :

sqlite3 /var/tmp/vthttpd.dat < /var/tmp/monfichier.sql

Et on grep sur ce qu’on souhaite

select j.JOB_SID, e.NAME, a.NAME, j.NAME, j.COMMENT, j.FAMILY, j.SCRIPT, h.NAME, u.NAME, q.NAME, d.NAME, GROUP_CONCAT(p.VALUE,'|')
from jobs j
left join applications a on j.APP_SID = a.APP_SID
left join environments e on a.ENV_SID = e.ENV_SID
left join hosts h on j.HOST_SID = h.HOST_SID or (ifnull(j.HOST_SID,'') = '' and ( a.HOST_SID = h.HOST_SID or (ifnull(a.HOST_SID,'') = '' and e.HOST_SID = h.HOST_SID) ) )
left join users u on j.USER_SID = u.USER_SID or (ifnull(j.USER_SID,'') = '' and ( a.USER_SID = u.USER_SID or (ifnull(a.USER_SID,'') = '' and e.USER_SID = u.USER_SID) ) )
left join queues q on j.QUEUE_SID = q.QUEUE_SID or (ifnull(j.QUEUE_SID,'') = '' and ( a.QUEUE_SID = q.QUEUE_SID or (ifnull(a.QUEUE_SID,'') = '' and e.QUEUE_SID = q.QUEUE_SID) ) )
left join dates d on j.DATE_SID = d.DATE_SID or (ifnull(j.DATE_SID,'') = '' and ( a.DATE_SID = d.DATE_SID or (ifnull(a.DATE_SID,'') = '' and e.DATE_SID = d.DATE_SID) ) )
left join job_parameters p on j.JOB_SID = p.JOB_SID
group by j.JOB_SID
;

ex.

root@acf59c2005cf:/mnt/workspace/temp# sqlite3 vthttpd.dat < all_jobs.sql
JOBac1100030a411a8d570cf93000000004|exploitation|appli_test|job2|||jobok.bat|client_local|vtom|queue_ksh|date_exp|
JOBac1100033f6aedb3570cf93000000002|exploitation|appli_test|job1|||jobok.bat|client_local|vtom|queue_ksh|date_exp|param1|param2|param3
JOBac1100036f024b9d570cf93000000006|exploitation|appli_test|job3|||jobok.bat|client_local|vtom|queue_ksh|date_exp|

(exemple en Java, P.I je ne suis pas expert en Java - je suis tombé sur des machines qui n’avaient pas sqlite3 … Ca ne suit surement pas les bonnes pratiques JAva mais ça fonctionne)

package top100_vthttpd_jobs_to_csv;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Top100Main {

	public static void main(String[] args) {
		Connection c = null;
		Statement stmt = null;
		String sepCSV = "," ;
		
		try {
			Class.forName("org.sqlite.JDBC");
			c = DriverManager.getConnection("jdbc:sqlite:D:\\workspace\\java\\top100_vthttpd_jobs_to_csv\\vthttpd.dat");
			c.setAutoCommit(false);
			stmt = c.createStatement();
			
			String allJobs = "select j.JOB_SID, e.NAME, a.NAME, j.NAME, j.COMMENT, j.FAMILY, j.SCRIPT, h.NAME, u.NAME, q.NAME, d.NAME, GROUP_CONCAT(p.VALUE,'|')"
					+ " from jobs j"
					+ " left join applications a on j.APP_SID = a.APP_SID"
					+ " left join environments e on a.ENV_SID = e.ENV_SID"
					+ " left join hosts h on j.HOST_SID = h.HOST_SID or (ifnull(j.HOST_SID,'') = '' and ( a.HOST_SID = h.HOST_SID or (ifnull(a.HOST_SID,'') = '' and e.HOST_SID = h.HOST_SID) ) )"
					+ " left join users u on j.USER_SID = u.USER_SID or (ifnull(j.USER_SID,'') = '' and ( a.USER_SID = u.USER_SID or (ifnull(a.USER_SID,'') = '' and e.USER_SID = u.USER_SID) ) )"
					+ " left join queues q on j.QUEUE_SID = q.QUEUE_SID or (ifnull(j.QUEUE_SID,'') = '' and ( a.QUEUE_SID = q.QUEUE_SID or (ifnull(a.QUEUE_SID,'') = '' and e.QUEUE_SID = q.QUEUE_SID) ) )"
					+ " left join dates d on j.DATE_SID = d.DATE_SID or (ifnull(j.DATE_SID,'') = '' and ( a.DATE_SID = d.DATE_SID or (ifnull(a.DATE_SID,'') = '' and e.DATE_SID = d.DATE_SID) ) )"
					+ " left join job_parameters p on j.JOB_SID = p.JOB_SID"
					+ " group by j.JOB_SID"
				//	+ " limit 10"
					+ " ;"
			;

			ResultSet rs = stmt.executeQuery( allJobs );
			
			
			while ( rs.next() ) {
			   String jSID = rs.getString(1);
			   String eName = rs.getString(2);
			   String aName = rs.getString(3);
			   String jName = rs.getString(4);
			   String jComment = rs.getString(5);
			   String jFamily = rs.getString(6);
			   String jScript = rs.getString(7);
			   String hName = rs.getString(8);
			   String uName = rs.getString(9);
			   String qName = rs.getString(10);
			   String dName = rs.getString(11);
			   String pValue = rs.getString(12);
			   System.out.print( 
					   //jSID + sepCSV + 
					   eName + sepCSV + aName + sepCSV + jName + sepCSV + jComment + sepCSV +
					   jFamily + sepCSV + jScript + sepCSV +hName + sepCSV + uName + sepCSV +
					   qName + sepCSV + dName + sepCSV + pValue
					);
			   System.out.println();
			}
		
			rs.close();
			stmt.close();
			c.close();
			
		}catch ( Exception e ) {
		  System.err.println( e.getClass().getName() + ": " + e.getMessage() );
		  System.exit(0);
		}

	}
}

Filtrer directement un fichier XML avec un fichier XSL et sortie en texte (pour du csv)

J’avais déjà utilisé ce langage de transformation XSL mais il m’était complétement sorti de la tête. Merci à JOEY de m’avoir inspriré ! :)

Fichier XSL de transformation (à adapter selon vos besoin)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="utf-8" />
  
  <!-- j'utilise cette sortie pour constituer mon fichier CSV utilisé en tant que data pour load dans Oracle une table avec SQLLDR et un fichier de controle .ctl -->
  <!--
  sqlldr do not set OPTIONALLY ENCLOSED BY '"' because this char appears in comments and params 

        LOAD DATA APPEND
        INTO TABLE nom_dela_table_oracle
        FIELDS TERMINATED BY "£"
        
        (
          "VTENVNAME"                     CHAR,
          "VTAPPLNAME"                    CHAR,
          "VTJOBNAME"                     CHAR,
          "APPMODE"                     CHAR,
          "JOBMODE"                     CHAR, 
          etc...
        )
  -->
  
  <xsl:param name="delimCSV" select="'£'" /><!-- j'utilise ce char car il n'est pas utilisé dans mon référentiel VTOM -->
  <xsl:param name="delimParam" select="'§'" />
  <xsl:param name="break" select="'&#xA;'" />
  <!--
  <xsl:value-of select="$delimCSV" />
   -->

  <xsl:template match="/">
	  <xsl:apply-templates select="Domain/Environments/Environment/Applications/Application/Jobs/Job" />
  </xsl:template>

  <!-- All Job node for root node -->
  <xsl:template match="Domain/Environments/Environment/Applications/Application/Jobs/Job">
    <xsl:value-of select="../../../../@name" /><xsl:value-of select="$delimCSV" />              <!-- EnvironmentName -->
    <xsl:value-of select="../../@name" /><xsl:value-of select="$delimCSV" />                    <!-- ApplicationName -->
    <xsl:value-of select="@name" /><xsl:value-of select="$delimCSV" />                          <!-- JobName -->
    <xsl:value-of select="../../@mode" /><xsl:value-of select="$delimCSV" />                    <!-- ApplicationMode -->
    <xsl:value-of select="@mode" /><xsl:value-of select="$delimCSV" />                          <!-- JobMode -->
    <xsl:value-of select="../../@onDemand" /><xsl:value-of select="$delimCSV" />                <!-- ApplicationOnDemand -->
    <xsl:value-of select="@onDemand" /><xsl:value-of select="$delimCSV" />                      <!-- JobOnDemand -->
    <xsl:value-of select="Script/text()" /><xsl:value-of select="$delimCSV" />                  <!-- Script -->
    <xsl:apply-templates select="Parameters/Parameter" /><xsl:value-of select="$delimCSV" />    <!-- Parameters -->
    <xsl:value-of select="../../../../../../@name" /><xsl:value-of select="$delimCSV" />        <!-- DomainName -->
    <xsl:value-of select="$break" />
  </xsl:template>

  <!-- All Parameter node for Job node -->
  <xsl:template match="Parameters/Parameter">
    <xsl:value-of select="text()" /><xsl:value-of select="$delimParam" />                       <!-- Parameter -->
  </xsl:template>  

</xsl:stylesheet>

Pour appliquer le fichier XSL au fichier XML vtexport VTOM, on peut faire ça assez simplement avec une class Java (Stylizer) fournie par Oracle.

Si vous voulez plus d’information sur la classe et la télécharger, voici le lien Oracle Transforming XML Data with XSLT.

# Compiler le code source java pour créer votre class Stylizer.class, A ne faire qu'une fois. La classe doit se trouver dans votre CLASSPATH (variable d'environnement)
javac Stylizer.java

# Lancer la commande pour transformer votre XML avec le fichier XSL
java Stylizer votrefichier.xsl vtorefichier.xml

Parser le vtexport XML VTOM en “pur” Java (un peu long)

package vtexport_xml_to_csv;

import java.io.File;
import java.io.FileWriter ;
import java.io.IOException;
import java.util.Date;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathConstants ;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

public class VtexportXML {
	private static String xmlPath ;
	private static Document document;
	private static final String separateurCSV = "£" ;
	private static final String separateurEscape = "§" ;
	private static String domainName;
	
	public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
		if (args.length == 1) {
			setXmlPath(args[0]);
		}else{
			System.err.println("Usage : 1 paramètre nécessaire");
			System.err.println("1 : chemin complet du fichier xml");
	        System.exit(1);
		}

        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();    	
        final DocumentBuilder builder = factory.newDocumentBuilder();
	    setDocument( builder.parse( new File( getXmlPath() ) ) );
    	listAllJobs();
	}

	public static String getXmlPath() {
		return xmlPath;
	}

	public static void setXmlPath(String xmlPath) {
		VtexportXML.xmlPath = xmlPath;
	}
	
	public static Document getDocument() {
		return document;
	}

	public static void setDocument(Document document) {
		VtexportXML.document = document;
	}
	
	public static void listAllJobs() throws XPathExpressionException, IOException{
		System.out.println(new Date()) ;
		
		File file = new File(getXmlPath() + ".txt");
		// creates the file
		file.createNewFile();
		// creates a FileWriter Object
		FileWriter writer = new FileWriter(file); 
		
		// xPath instance
		XPath xPath =  XPathFactory.newInstance().newXPath();
		// set DomainName 
		setDomainName((String) xPath.compile("/Domain/@name").evaluate( getDocument() ) );
		
		// get all nodelist job
		NodeList nodeList = (NodeList) xPath.compile("/Domain/Environments/Environment/Applications/Application/Jobs/Job").evaluate(getDocument(),XPathConstants.NODESET);	
		
		int percentDone = 1 ;
		System.out.print("0%.");
		
		// loop over nodelist job
		for(int i = 0; i < nodeList.getLength() ; i++){
			
			// print evolution running script %
			if(! ( (i * 100 / nodeList.getLength()) < percentDone ) ){
				percentDone++ ;
				if(percentDone % 10 == 0){
					System.out.print(percentDone + "%");
				}else{
					System.out.print(".");
				}
			}
			
			// job node
			Node jobNode = (Node) nodeList.item(i) ;
			
			// nodelist parameters
			NodeList parametersNodes = (NodeList) xPath.evaluate("Parameters/Parameter",jobNode,XPathConstants.NODESET) ;
			String[] parameters = new String [ parametersNodes.getLength() ] ;
			
			// all parameters as an Array
			for(int j = 0 ; j < parametersNodes.getLength() ; j++){
				parameters[j] = ((Node) parametersNodes.item(j)).getTextContent() ;
			}
			
			writer.write(
				
				separateurEscape + xPath.evaluate("../../../../@name", jobNode) + separateurEscape + separateurCSV +	// envName
				separateurEscape + xPath.evaluate("../../@name", jobNode) + separateurEscape + separateurCSV +			// appName
				separateurEscape + xPath.evaluate("@name", jobNode) + separateurEscape + separateurCSV +				// jobName
				separateurEscape + xPath.evaluate("../../@mode", jobNode) + separateurEscape + separateurCSV +			// AppMode
				separateurEscape + xPath.evaluate("@mode", jobNode) + separateurEscape + separateurCSV +				// JobMode
				separateurEscape + xPath.evaluate("../../@onDemand", jobNode) + separateurEscape + separateurCSV +		// AppOnDemand
				separateurEscape + xPath.evaluate("@onDemand", jobNode) + separateurEscape + separateurCSV +			// JobOnDemand
				separateurEscape + xPath.evaluate("Script",jobNode) + separateurEscape + separateurCSV +				// Script
				separateurEscape + String.join("|", parameters) + separateurEscape + separateurCSV + 					// Parameters
				separateurEscape + getDomainName() + separateurEscape + separateurCSV									// DomainName
				+ "\n"
			);
			
		}

		// Flush the content to the file
		writer.flush();
		writer.close();
		
		// print prompt running script
		System.out.println();
		System.out.println("Fichier en sortie : " + file.getAbsolutePath());
		System.out.println(new Date() + " The end");
	}

	public static String getDomainName() {
		return domainName;
	}

	public static void setDomainName(String domainName) {
		VtexportXML.domainName = domainName;
	}

}

Autre pour moi

Reminder pour moi : consolidation des statistiques VTOM (en attendant que tout soit dans la base postgres, je passe par le vthttpd dump)

plateforme=up2
vthttpdDat=vthttpd_${plateforme}.dat
allJobsSQL=all_jobs.sql
allJobs1to1=all_jobsSID_1to1Param_${plateforme}.txt
allJobsManyTo1=all_jobsSID_manyTo1Param_${plateforme}.txt
outputStats=stats_${plateforme}
vtsgbdPort=30509
hostFilter=HOST1

test -f $vthttpdDat && rm $vthttpdDat
vthttpd -dump $vthttpdDat
sqlite3 $vthttpdDat < $allJobsSQL |  awk 'BEGIN{ FS="|" ; OFS="|" } { l=length($10) ; if(l == 2) { $10="0"$10 ;} ;  if(l == 1){ $10="00"$10 }  ; print}' | sort -g  >  $allJobs1to1

### All jobs manyto1 parameters
awk -F "|" 'BEGIN{ job_sid=null;} {if($1 == job_sid){ printf "|%s",$11 }else{ if(job_sid != null){ printf "\n" } ; printf "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s",$1,$2,$3,$4,$5,$6,$7,$8,$9,$11 } ; job_sid=$1 ;}' $allJobs1to1 | sort > $allJobsManyTo1

# get stats with filters (modify where clauses)   
# select vtobjectsid,vtenvname, vtapplname, vtjobname, vtbegin, (vtend::timestamp - vtbegin::timestamp), vtend, vtexpdatevalue, vthostname, vtusername, vtbqueuename, vtdatename,vtstatus, vterrmess  
~/sgbd/bin/psql -d vtom -p $vtsgbdPort << EOF
\pset tuples_only
\pset footer off
\a
\o $outputStats
select vtobjectsid,vtbegin,(vtend::timestamp - vtbegin::timestamp),vtend,vtexpdatevalue,vtstatus,vterrmess
from vt_stats_job 
where vthostname = '$hostFilter' 
and (vtend::timestamp - vtbegin::timestamp) >= '00:10:00'
order by (vtend::timestamp - vtbegin::timestamp) 
;
EOF

Très long avec Awk (beaucoup d’itération :x)

awk -v fic=$allJobsManyTo1 'BEGIN{
    OFS="|";FS="|";
    i=0 ;
    while ((getline line < fic ) > 0){
        tAllJobsManyTo1[i]=line ;
        i++ ;
    }
    close (fic) ;
    
}{
    if($1 ~ /^APP/){
      print $0;
      next
    }
    jobSID=$1 ;
    lineStats=$0 ;
    
    for (i in tAllJobsManyTo1){
        split(tAllJobsManyTo1[i],tLine,"|");
   
        if( tLine[1] == jobSID ){
          script=tLine[5] ;
          printf "%s|%s\n",lineStats,script ;
          delete  tAllJobsManyTo1[i] ;
          break; 
        }
    }
    
}' /var/tmp/stats

Beaucoup plus rapide avec Pandas - Python :

import pandas

my_cols_csv1 = [ "vtobjectsid","vtbegin","vtduration","vtend","vtexpdatevalue","vtstatus","vterrmess" ]
csv1 = pandas.read_csv('stats_up2',delimiter=r"|",names=my_cols_csv1)
my_cols=["vtobjectsid","env","app","job","script","host","user","queue","vtdatename","param"]
my_cols=my_cols + range(150)
csv2 = pandas.read_csv('all_jobsSID_manyTo1Param_up2.txt',names=my_cols, sep="|")
merge = pandas.merge(csv1,csv2, on='vtobjectsid')
merge.to_csv("output.csv")
	vtobjectsid	vtbegin	vtduration	vtend	vtexpdatevalue	vtstatus	vterrmess	env	app	job	script	host	user	queue	vtdatename	param
0	JOB7ef5b39400006ad956a6113e00014e22	19/03/2016 03:58	00:10:00	19/03/2016 04:08	19/03/2016	3	Termine a 04:08:30	ENV1	poz12daj1	p800ostp1	PATH_DWH_SHELL/OSEF_odi.ksh	HOST1	prdwh12	queue_ksh	ENV1J_12_rdwh	
1	JOB7ef5b39400006ad956a6113e00014e22	03/03/2016 03:29	00:12:24	03/03/2016 03:42	03/03/2016	3	Termine a 03:42:17	ENV1	poz12daj1	p800ostp1	PATH_DWH_SHELL/OSEF_odi.ksh	HOST1	prdwh12	queue_ksh	ENV1_12_rdwh