Comment modifier la rétention des logs des JVMs sous Websphere en utilisant python ?

Premièrement, je lance une recherche sur Google avec les mots clés suivants :

change log file rotation and maximum file size jvm logs websphere

Je vois que cela ne suffit pas car je tombe sur les méthodes manuelles et non les scripts python. Du coup j’enrichis ma recherche :

Setting log file size and rotation for JVM log files websphere jython wsadmin

Super ! Je tombe sur un article qui a l’air de répondre à ma problématique.

# rotateWasLogEPRN.py
# Setup WAS Log file rotations
# for yourserver
#
# Implement:
# ./wsadmin.sh -lang jython -f $FILE_LOCATION/rotateWasLogEPRN.py >> $TO_LOG_FILE
#
# You can add this as you have other scripts in the deployment script
# if you need help please let me know.
#
#

print "Changing the SystemOut & SystemErr log file rotation settings"
var1 = AdminConfig.getid('/Cell:yourcell/Node:yournode/Server:yourserver/')

log = AdminConfig.showAttribute(var1, 'outputStreamRedirect')
log2 = AdminConfig.showAttribute(var1, 'errorStreamRedirect')

AdminConfig.modify(log, '[[rolloverSize 10] [maxNumberOfBackupFiles 5]]')
AdminConfig.modify(log2,'[[rolloverSize 10] [maxNumberOfBackupFiles 5]]')

AdminConfig.save()

print "New SystemOut settings 9-10-08"
AdminConfig.show(log).split("\n")
print "New SystemErr settings 9-10-08"
AdminConfig.show(log2).split("\n")

Je vais l’adapter à mes besoins, car j’ai besoin d’une rotation tous les 5MB et je dois garder 20 fichiers de logs. Cela donne donc :

# rotateWasLogEPRN.py
# Setup WAS Log file rotations
# for yourserver
#
# Implement:
# ./wsadmin.sh -lang jython -f $FILE_LOCATION/rotateWasLogEPRN.py >> $TO_LOG_FILE
#
# You can add this as you have other scripts in the deployment script
# if you need help please let me know.
#
#

print "Changing the SystemOut & SystemErr log file rotation settings"
var1 = AdminConfig.getid('/Cell:yourcell/Node:yournode/Server:yourserver/')

log = AdminConfig.showAttribute(var1, 'outputStreamRedirect')
log2 = AdminConfig.showAttribute(var1, 'errorStreamRedirect')

AdminConfig.modify(log, '[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')
AdminConfig.modify(log2,'[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')

AdminConfig.save()

print "New SystemOut settings 6-12-17"
AdminConfig.show(log).split("\n")
print "New SystemErr settings 6-12-17"
AdminConfig.show(log2).split("\n")

De plus je dois lancer ce script sur 48 JVMs. Il faut donc que je trouve un moyen de faire une boucle en python ou d’encapsuler ça dans du shell, ou encore de créer des fonctions en python. Je vais utiliser la dernière méthode, les fonctions en python. J’ai déjà vu un de mes collègues le faire, cela ne devrait pas être très compliqué.

def rotateWasLog(nodename,servername):
 server_id = getServerByNodeAndName(nodename,servername)
 log = AdminConfig.showAttribute(server_id, 'outputStreamRedirect')
 log2 = AdminConfig.showAttribute(server_id, 'errorStreamRedirect')
 AdminConfig.modify(log, '[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')
 AdminConfig.modify(log2,'[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')

# appel fonction
rotateWasLog('monPremierNode','SERVER01')
rotateWasLog('monPremierNode','SERVER02')

print "New SystemOut settings 6-12-17"
AdminConfig.show(log).split("\n")
print "New SystemErr settings 6-12-17"
AdminConfig.show(log2).split("\n")

AdminConfig.save()
AdminNodeManagement.syncActiveNodes()

Mon script ne fonctionne pas du premier coup, comme d’habitude avec python j’ai des erreurs incompréhensibles :

Traceback (innermost last):
 File "<string>", line 9, in ?
 File "<string>", line 2, in rotateWasLog
NameError: getServerByNodeAndName

Ce que je sais c’est que c’est l’appel à la fonction qui est erroné.

C’est bon, j’ai trouvé, j’avais oublié d’inclure la librairie wsadminlib.py. Donc cela a fonctionné comme prévu. Ci-dessous mon code :

execfile('/xxxx/wsadminlib.py')

def rotateWasLog(nodename,servername):
 server_id = getServerByNodeAndName(nodename,servername)
 log = AdminConfig.showAttribute(server_id, 'outputStreamRedirect')
 log2 = AdminConfig.showAttribute(server_id, 'errorStreamRedirect')
 AdminConfig.modify(log, '[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')
 AdminConfig.modify(log2,'[[rolloverSize 5] [maxNumberOfBackupFiles 20]]')

# appel fonction
rotateWasLog("xxx","xxx")
rotateWasLog("xxx","xxx")

#print "New SystemOut settings 6-12-17"
#AdminConfig.show(log).split("\n")
#print "New SystemErr settings 6-12-17"
#AdminConfig.show(log2).split("\n")

AdminConfig.save()
AdminNodeManagement.syncActiveNodes()

Il ne me reste plus qu’à appeler la même fonction pour toutes mes JVMs.

Source :

Websphere : Sous Linux comment changer un paramètre personnalisé de la JVM

Quand vous avez une JVM, ou encore 2 ou 3, vous pouvez modifier manuellement un paramètre de la JVM sans problème via la console d’admin. Par contre si vous avez des trentaines voire plus de JVM à modifier, autant utiliser un script jython. C’est ce que je cherchais, un moyen d’automatiser et de sécuriser cette modification. En effet, via l’interface graphique de la console d’admin, une JVM est vite oubliée.

J’ai trouvé un script sur le site d’IBM DeveloperWorks, il suffit de lui passer quelques paramètres et le tour est joué.

Procédure :

  1. Vous déposez le script sur le serveur qui héberge le DMGR, la console d’admin.
  2. Vous lancez le script en indiquant bien le login et le mot de passe pour se connecter à la console wsadmin (en ligne de commande)
    • Paramètre possible : all => modifier toutes les JVMs websphere
    • Paramètre myNode:myServer => modifier une JVM d’un noeud précis
    • Paramètre myServer => modifier une JVM précise
  3. Vous vérifiez sur votre console d’admin graphique que la modification a bien été réalisée

Bien sûr vous pouvez englober le script dans un script shell global avec une boucle for ou juste une liste précise des JVMs à modifier.

Source : 

Script addJVMProperty.py sur IBM developerWorks

Récupérer un paramètre oracle sur plusieurs serveurs en ksh sous Linux

En mode sysadmin

Etant dans une équipe d’intégration, j’ai besoin de lancer souvent de petits scripts maison, telle une sysadmin. Je ne fais pas de dev à proprement parler, j’ai juste besoin d’un résultat rapide et efficace. Mon script n’a pas besoin d’être générique ou d’avoir un max de contrôles, c’est du « ONE SHOT » en quelque sorte.

Comment je procède ?

Dans un premier temps, comme tout sysadmin, je teste d’abord le script sql en local sur les machines.

Dans un second temps, je lance le script à distance toujours en ligne de commande.

Enfin, je crée un script ksh de quelques lignes qui contiendra tout ce dont j’ai besoin : liste des serveurs, script SQL, résultat affiché sur la console.

Je vous laisse découvrir le script ci-dessous, testé et approuvé sur des bases Oracle 11g :)

#!/usr/bin/ksh

for i in server1 server2 server3 server4 server5
do
ssh -T $i <<EOF
echo $i
#amélioration de l'affichage, 200 caractères par ligne
echo "set linesize 200 " > /tmp/scriptOracle.sql
echo "show parameter parallel_max_servers" >> /tmp/scriptOracle.sql
#se déconnecter de la base
echo "exit" >> /tmp/scriptOracle.sql
#sourcer le ficher d'environnement Oracle, lancer le  script sql créé juste en haut et récupérer uniquement la ligne qui nous intéresse
        su - oracle -c ". /chemin/fichierParamOracle  ; sqlplus / as sysdba @/tmp/scriptOracle.sql" 2>/dev/null | grep parallel_max_servers
EOF
done

Résultat : 
monPrompt> ./scriptOracle.ksh
server1
parallel_max_servers                 integer                          xxx
server2
parallel_max_servers                 integer                          xxx
server3
parallel_max_servers                 integer                          xxx
server4
parallel_max_servers                 integer                          xxx
server5
parallel_max_servers                 integer                          xxx
Petit script de sysadmin

Récupérer la mémoire utilisée par une JVM sous Websphere 8.5.5 avec wsadmin

Scripting en jython

Si vous avez suivi mes dernières aventures, vous savez que je travaille énormément sur Websphere en ce moment et plus particulièrement sur la partie wsadmin de websphere, à savoir la partie scripting. Mes anciens articles sur websphere.

Coeur du script : la récupération de la valeur de la mémoire utilisée par notre JVM. Pensez à remplacer xxServer par le nom de votre JVM.

perfName = AdminControl.completeObjectName ('type=Perf,process=xxServer,*')
perfOName = AdminControl.makeObjectName (perfName)
sigs = ['javax.management.ObjectName', 'java.lang.Boolean']
jvmName = AdminControl.completeObjectName ('type=JVM,process=xxServer,*')
params = [AdminControl.makeObjectName (jvmName), java.lang.Boolean ('false')]
jvmStats=AdminControl.invoke_jmx (perfOName, 'getStatsObject', params, sigs)
UsedMemory=jvmStats.getStatistic( 'UsedMemory').getCount()
print UsedMemory

Comment appeler ce bout de script ? Il faut appeler le script wsadmin.sh de websphere qui lui, déroulera le script ci-dessus. Les paramètres de wsadmin sont détaillés dans cet article.

#!/bin/bash

"$WAS_ROOT/bin/wsadmin.sh" $ADMIN_OPT -lang jython -f `dirname $0`/usedMemory.py

Ensuite on peut faire une boucle pour afficher la valeur de la mémoire utilisée toutes les 5 secondes, avec en prime la date :

import sys
import time
import re

count=3
while count > 0 :
## MAIN
 perfName = AdminControl.completeObjectName ('type=Perf,process=xxServer,*')
 perfOName = AdminControl.makeObjectName (perfName)
 sigs = ['javax.management.ObjectName', 'java.lang.Boolean']
 jvmName = AdminControl.completeObjectName ('type=JVM,process=xxServer,*')
 params = [AdminControl.makeObjectName (jvmName), java.lang.Boolean ('false')]
 jvmStats=AdminControl.invoke_jmx (perfOName, 'getStatsObject', params, sigs)
 UsedMemory=jvmStats.getStatistic( 'UsedMemory').getCount()
 now = time.localtime(time.time())
 print time.strftime("%d/%m/%y %H:%M:%S", now),";",UsedMemory
 time.sleep(5)

Sources : 

Python : Date et parsing de texte

Programmation en python

Pour les besoins d’un script en python, j’ai besoin de petits bouts de code. En effet, suite à mon article sur websphere, j’ai besoin de parser des résultats et de les stocker dans un fichier texte. On va voir ensemble comment procéder.

Récupération de la date du jour :

import time
# Obtenir l'heure et la date locale
now = time.localtime(time.time())

print time.strftime("%y/%m/%d %H:%M", now)
date en python

ça affiche :

16/09/09 09:07

Ensuite, ce que je veux faire, c’est parser la ligne ci-dessous et récupérer uniquement la valeur de UsedMemory :

jvmRuntimeModule | HeapSize=524288;;;; FreeMemory=205940;;;; UsedMemory=318347;;;; UpTime=1356524;;;; ProcessCpuUsage=0;;;;

Je vous rappelle comme j’avais fait en ksh pour parser :

cat resultat | grep UsedMemory | awk -F"=" ' { print $4 }' | awk -F";" ' { print $1 }'
Parsing en ksh

On va décomposer étape par étape, je vais commencer par utiliser la commande split et lui indiquer divers séparateurs. Je l’avais déjà utilisé lors d’un précédent article, mais bon, ça fait déjà 4 ans et demi !

import re

example = "jvmRuntimeModule | HeapSize=524288;;;; FreeMemory=205940;;;; UsedMemory=318347;;;; UpTime=1356524;;;; ProcessCpuUsage=0;;;;"

parts = example.split()
print parts[4]
other = parts[4].split('=')
print other[1]
fin = other[1].split(';')
print fin[0]
parsing step by step

Voilà le résultat :

UsedMemory=318347;;;;
318347;;;;
318347

Optimisation de code

C’est un code qui fonctionne, me direz-vous, mais peut-on faire ça en deux lignes au lieu de trois ?

import re

example = "jvmRuntimeModule | HeapSize=524288;;;; FreeMemory=205940;;;; UsedMemory=318347;;;; UpTime=1356524;;;; ProcessCpuUsage=0;;;;"

partie = example.split(';')
print partie[8]
valeur = partie[8].split('=')
print valeur[1]

Résultat :

 UsedMemory=318347
318347

Il faut tout de même que je teste la présence de UsedMemory :

import re

example = "jvmRuntimeModule | HeapSize=524288;;;; FreeMemory=205940;;;; UsedMemory=318347;;;; UpTime=1356524;;;; ProcessCpuUsage=0;;;;"
if "UsedMemory" in example:
 partie = example.split(';')
 print partie[8].split('=')[1]

Si on revient à mon code initial, il faut que j’écrive sur la même ligne la date et la valeur d’UsedMemory. Pour l’instant, le code ci-dessous ne fonctionne pas !

for stat in statsList:
  if "UsedMemory" in str(stat):
   now = time.localtime(time.time())
   partie = stat.split(';')
   print time.strftime("%d/%m/%y %H:%M", now) , partie[8].split('=')[1]
   time.sleep(5)
Belle erreur python !

 

Traceback (innermost last):
  File "<string>", line 64, in ?
TypeError: string member test needs char left operand

Recherche Google

Du coup, je fais une petite recherche sur Google et je tombe sur l’excellent site stackoverflow. Je rajoute au passage un séparateur ; entre mes différents champs, ce qui me génèrera du « pseudo csv ».

for stat in statsList:
  if stat.find('UsedMemory=') >= 0:
   now = time.localtime(time.time())
   partie = stat.split(';')
   print time.strftime("%d/%m/%y %H:%M", now),";",partie[8].split('=')[1]
   time.sleep(5)

Résultat :

09/09/16 10:29 ; 383886
09/09/16 10:29 ; 383886
09/09/16 10:29 ; 385079
...
09/09/16 10:31 ; 396919
09/09/16 10:31 ; 396919
09/09/16 10:31 ; 398375
09/09/16 10:31 ; 396919

Pour la partie écriture dans un fichier de logs, pour l’instant je vais utiliser ksh :)

Sources :