mercredi 3 septembre 2014

Toodledo Sync : Fin de l'aventure

Tout d'abord, merci à tous pour vos commentaires et suggestions, merci aux près de 1.000 utilisateurs quotidiens (la semaine, un peu moins le week-end :-), merci merci !

Malheureusement, je n'ai plus de temps à consacrer à la maintenance et au développement de cette extension...

Bonne continuation à tous!

-------------------------------------------------------------------------------------------------------------------------
First of all, thanks a lot for all your comments and suggestions, many thanks to the near 1.000 daily users, thank you all !

Unfortunately, I no longer have the spare time I had to maintain and add fonctionnalities to this little extension...

Best regards !

Talend : comment passer des variables entre job et sous job

Quel est le problème ?
Il est parfois nécessaire de pouvoir récupérer des informations depuis un sous-job par le job parent. Cette procédure n'est pas évidente à mettre en place, voici donc comment faire...

L'exemple utilisé ci-dessous n'est peut-être pas le meilleur, mais il illustre le propos. Je souhaite surveiller la présence d'une table particulière dans une base de données, et attendre sa présence avant de poursuivre l'exécution des jobs suivants. Si vous avez une meilleure solution pour réaliser cela, je suis bien sûr preneur.

Bref, allons-y :

Un peu de théorie...

Je vais essayer de vulgariser un peu des concepts de programmation java, qui sont nécessaires pour comprendre le mécanisme utilisé par talend.

Dans talend, deux (à ma connaissance) objets permettent de stocker de l'information de manière globale, et de la faire transiter d'un job à un autre : les contextes, et le globalMap.

Malheureusement (ou pas), lorsque l'on passe une variable d'un job vers un sous job, celle-ci est passée en valeur, pas en référence. La valeur de la variable est ainsi passée au sous-job, et si celui-ci en modifie la valeur, cette valeur modifiée ne sera accessible que dans le sous job. Le job père, lui a accès à sa propre valeur de la variable.

Cela est vrai pour les objets "immuables" (dont font partie les entiers et les chaines, par exemple). Le seul moyen de contourner cette limitation, est de passer au sous-job, une référence vers un objet "mutable".

Il faut donc utiliser un objet "mutable", mais aussi utilisable en multi-thread. Pour cela, on utilise un ConcurrentHashMap. Ouf, fini pour la théorie.


Un peu de pratique...

Tout ceci étant maintenant extrêmement clair (sinon, ce n'est pas grave), voici comment réaliser cette opération.

Job père

Ce job devra passer une variable au sous-job, puis lire la variable modifiée renvoyée par le sous-job.

Job père avec tJava1 -> sous-job -> tJava2
Job père


Déclaration de la variable

Le premier élément clef est le tJava "set variable". Ce tJava défini la variable que nous allons passer à notre sous-job, avec le code suivant :

// On defini un concurrent hashmap qui sera accessible par le fils de ce job
globalMap.put("Go_SB_SharedMap", new java.util.concurrent.ConcurrentHashMap());


En résumé, dans le globalMap de talend, on crée un objet de type ConcurrentHasMap, qui s'appelle Go_SB_SharedMap.

Lecture de la valeur de la variable

Le deuxième élément clef est le tJava "get variable". Ce tJava récupère la valeur qui a été définie dans le sous-job, de la manière suivante :

// On recupere le contenu de la variable definie dans notre childjob
System.out.println("Valeur de retour du sous job : " + ((java.util.Map) globalMap.get("Go_SB_SharedMap")).get("tableOK") );


(pour les curieux, en fait j'utilise cette valeur de retour dans le tLoop pour sortir de la boucle une fois que le sous job indique que la table est présente)

Passage de la variable au sous-job

Bien évidemment, entre les deux opérations, on aura passé la variable au sous-job, qui lui l'aura modifiée. Pour passer la variable au sous-job, on utilise (évidemment) les contextes. On défini donc une variable de contexte dans les paramètres du tRunJob :

Contexte 'built-in' du sous-job
Contexte du tRunJob (sous-job)


et la valeur de cette variable de contexte (ici, je l'ai nommée avec le même nom que la variable que l'on passe, mais cela n'est pas obligatoire, et même peut porter à confusion), est tout simplement le hashmap que l'on aura défini dans notre premier tJava.

Accès et modification de la variable dans le sous-job

sous-job avec un tJava qui modifie la variable passée par le job père
Sous-job


Ici mon sous-job fait une petite requête sur une table, et s'il n'y a pas d'erreur (donc si la table existe), met à jour la variable grace au tJava, qui fait tout simplement :

// On met a jour la variable dans le shared map pour que le père de ce job récupère l'info :
((java.util.Map) context.Go_SB_SharedMap).put("tableOK", "1");



Et voila!
Facile, non?

lundi 10 décembre 2012

Créer ses propres graphes sous cacti

Voici le challenge du jour : pouvoir comparer pour nos switchs réseaux, le trafic en unicast et le reste (broadcast et multicast).

Cacti fourni par défaut les graphes suivants : non-unicast packets et un autre graphe unicast packets.
Nous souhaitons voir sur le même graphe, les paquets unicast et les autres, pour comparaison rapide. C'est assez simple, mais implique de manipuler plusieurs objets cacti, et mérite donc un petit pense bête...

1ère étape : créer un "Data Template"

Le data template va définir quelles informations nous intéressent dans notre graphe. Rien de très compliqué ici, on se base par exemple sur le template pré défini "Interface - Unicast Packets" et on l'enrichi (veuillez excusez le mélange franglais dans les titres...) :



Et on lui ajoute 4 "Data Source Item"s :



Voila, ça c'est bon.

2ème étape : créer une "Data Query"

L'avantage de créer une Data Query, est qu'il est ainsi possible d'associer nos requêtes de données à une interface, cela permettra de générer le graphe pour chaque interface des switchs et non pas de générer un graphe par switch. Donc, dans la console cacti, il faut aller dans Data Queries, et sélectionner "SNMP - Interface Statistics", pour ajouter notre définition dans les requêtes associées aux interfaces.
Une fois la "Data query" "SNMP - Interface Statistics" sélectionnée, on lui ajoute un nouveau "Associated Graph Templates". Les suggested values en dessous sont importantes pour générer automatiquement le nom des data sources et des graphes avec le "nom" de l'interface du switch.




3ème étape : Créer un graph template, qui va utiliser notre data query pour faire des jolies choses :

Pas de gros mystère ici, voici la définition du contenu du graphe : le unicast en LINEx et le broadcast en AREA. La première ligne en en LINE3 car plus épaisse, elle se verra même si la deuxième ligne (en LINE1) est superposée.



Les autres valeurs du graphe sont elles aussi classiques :



Et voilà, tout est prêt pour avoir un graphe avec nos quatre éléments facilement comparables :


C'est quand même plus parlant qu'un graphe avec le unicast d'un coté, et un graphe avec le broadcast de l'autre, non ?








vendredi 7 décembre 2012

pure-ftpd Erreur 530 : Sorry but I can't trust you

Cette erreur est un peu sioux : elle se produit lorsqu'on a paramétré un utilisateur virtuel pure ftpd, qui est lié à un utilisateur ou un groupe physique avec un uid ou gid inférieur à 1000.

En effet, pure-ftpd interdit le login pour les numéros inférieurs à celui paramétré dans /etc/pure-ftpd/conf/MinUID.

Donc si vous mappez un utilisateur 'système' à un utilisateur virtuel, par exemple www-data (uid 33 et gid 33 pour ubuntu) :

sudo pure-pw useradd ftpupload -u www-data -g www-data -d /var/www/

Il faut modifier /etc/pure-ftpd/conf/MinUID, et mettre 32 par exemple, puis redémarrer le serveur : sudo service pure-ftpd restart (ou /etc/init.d/pure-ftpd restart).

Et voilà !

lundi 3 décembre 2012

Cacti : état d'un hôte dans la carte réseau

Pour ajouter une information sur le statut de l'hôte dans weathermap (avec cacti), c'est très très simple.

Le plugin (installé avec le plugin weathermap pour cacti) "cactihost" permet de renvoyer l'état d'un hôte de cacti. Il renvoie pour un hôte, les valeurs suivantes :
  • 0 : désactivé
  • 1 : down
  • 2 : en cours de remise en route
  • 3 : up
Pour utiliser les résultats de ce 'plugin' dans une weathermap, il faut aller éditer la carte à la main (l'éditeur graphique ne permettant pas d'utiliser toutes les fonctionnalités de weathermap).

Dans mon cas : vim  /usr/share/cacti/site/plugins/weathermap/configs/MPLS

Il faut alors définir (en haut du fichier), l'échelle de couleur utilisée pour les statuts :

SCALE cactiupdown 0 0.5  192 192 192
SCALE cactiupdown 0.5 1.5 255 0 0
SCALE cactiupdown 1.5 2.5 0 0 255
SCALE cactiupdown 2.5 3.5 0 255 0

Puis, sur un hôte, lui dire d'utiliser cette échelle de couleur :

NODE test
      LABEL test
      TARGET cactihost:xx
      USESCALE cactiupdown in
      POSITION xx yy

Et voilà : si l'hôte est down, le label est rouge, s'il est up, il est vert, désactivé gris, recovering bleu.

Le numéro d'hôte se trouve dans la colonne id des devices de cacti.

Voici le résultat (je sais, je ne suis pas graphiste, mais les informations sont là)



A suivre avec des modifications d'icône en fonction du statut (mais ça, ce n'est pas critique :-)

jeudi 29 novembre 2012

Cacti : changer la couleur du graphe en fonction de la valeur

Bien, une ligne de limite (cf. précédent post), c'est déjà sympa, mais avoir des erreurs qui sautent aux yeux c'est mieux.

Si l'on prend l'exemple de la surveillance du ping précédent, l'idéal serait que le graphe change de couleur quand le ping dépasse la limite.

C'est assez simple :


1) Il faut créer un CDEF (Computer DEFinition il paraît) :
Dans cacti, console, graphs management, CDEFs, en ajouter un.
Sa définition est simple : cela doit renvoyer la valeur si elle est dans une certaine limite.
Pour cela, utiliser la définition suivante :
 
               cdef=CURRENT_DATA_SOURCE,0,60,LIMIT

en ajoutant les items suivants (ou en ajoutant une custom string complete) :



2) Il suffit maintenant de modifier le template du graph que nous souhaitons améliorer :
Ajouter un item de type AREA, avec comme CDEF la CDEF précédente :



L'ordre dans le graphe est important : les items sont dessinés du haut vers le bas dans l'ordre suivant :



Ainsi par défaut toutes les valeurs sont en 'AREA' (aire remplie) rouge, puis par dessus cette AREA rouge, les valeurs correspondantes à la CDEF précédente (donc les valeurs inférieures à 60) seront affichées en vert.

Et voila le résultat :


C'est plus parlant, non ?



Cacti : ajouter une limite sur les graphiques

Lorsque l'on vérifie plusieurs graphes de ping latency, avec une échelle automatique, il est impossible sans lire la légende du graphe de savoir si le ping est dans la norme ou pas.



Pour avoir l'information d'un coup d'oeil, il suffit d'ajouter dans le modèle de graphe, un ligne à une valeur donnée :

Dans mon cas, le "graph template" "Unix ping latency".

Faire ADD dans Graph Template Items [edit: Unix - Ping Latency]
Puis définir une entrée de la façon suivante :
Data source : on ne touche pas
Color : la couleur que vous souhaitez
Opacity : idem
Graph Item Type : HRULE
Consolidation Function : Average
CEDF : None
Value : la valeur que vous souhaitez, ici 80

Et voilà : d'un coup d'oeil on voit où on a des dépassements de la norme


mardi 18 septembre 2012

Copier automatiquement certains attachements dans kmail

Le défi du jour est le suivant :

KMail est installé sur un PC, et je veux que lorsque des mails avec un certain type de pièce jointes arrivent, ces pièces jointes soient automatiquement enregistrées dans un dossier particulier.

Très simple : dans KMail, il est possible de définir des filtres, et lorsque ces filtres sont activés, d'appeler une commande externe.

Un exemple de filtre correspondant à mon besoin est :

Le filtre doit correspondre à tous les critères suivants :
Sujet contient DOC
Emetteur contient <mon mail d'origine>
Dans ce cas, exécuter la commande /home/cmege/bin/kmail_save_doc

Il n'y a plus qu'à créer la commande kmail_save_doc, dont voici le contenu (un peu de perl ce coup ci) :

#!/usr/bin/perl

use Email::MIME;

my $line;
my $message='';


# on recupere l'entree (le contenu du mail) dans la variable message

while ($line=<>)
{
    $message.=$line;
}

my $email = Email::MIME->new($message);


# et on parcoure toutes les parties de ce message
$email->walk_parts(sub {

      my ($part) = @_;
      return if $part->subparts; # multipart
      

      # on recupere le type mime de la partie en cours de lecture
      my $type=$part->content_type;

      # et si c'est du ms-word, on l'enregistre
      if ( $part->content_type =~ m[application/ms-word]gi ) {
           my $body = $part->body;
            
           my $fichier = Email::MIME->invent_filename($type);
           $fichier = ">>/home/cmege/Documents/$fichier.doc"; 
           open (MYFILE, $fichier);
           print MYFILE $body;
           close (MYFILE); 
      }
  }
);


Rendre ce fichier exécutable : chmod a+x /home/cmege/bin/kmail_save_doc

Ne pas oublier d'installer la librairie perl permettant de traiter les mails : 

sudo apt-get install libemail-mime-perl

Et voilà!



mardi 10 juillet 2012

Des snapshots invisibles dans vmware

Nous avons rencontré un problème qui semble récurrent vu le nombre d'infos que l'on trouve sur le net à ce sujet.

Une machine virtuelle avait un snapshot en cours :
  • fichiers xxxx-000001.vmdk
  • dans le .vmx : ligne représentant le disque dur pointant sur un fichier scsi0:1.fileName = "xxxx_1-000001.vmdk" 
  • dans le .vmsd : définition de shapshot
Mais ce snapshot n'était pas visible dans l'interface de gestion et donc impossible à supprimer.

Les méthodes trouvées sur le net n'ont pas fonctionné pour nous, mais pour référence, je les cite :
  1. Créer un nouveau snapshot sur la machine, 
  2. Si tout va bien, le snapshot 'invisible' et le nouveau apparaissent,
  3. Supprimer tous les snapshots
Cette méthode n'a pas fonctionné pour nous.

Deuxième méthode : cloner la machine, cela doit supprimer tous les snapshots. Nous n'avons pas pu faire cette opération faute de place sur nos disques.

Voici la troisième méthode qui a fonctionné pour nous : redémarrer le contrôleur vmware. Et c'est tout...

Pourquoi cela a fonctionné pour nous : nous avions lancé un backup de la machine par VCB, et ce backup a planté en cours pour manque de place disque. La méthode de backup par VCB fonctionne de la manière suivante :
  1. Création d'un snapshot de la machine pour pouvoir accéder aux vmdk
  2. Montage de la machine là où est lancé la commande VCB
  3. Sauvegarde de la machine
Nous avons donc conclu que la machine était toujours "montée" sur notre contrôleur pour réaliser la sauvegarde, et un redémarrage du contrôleur a ainsi libéré ce point de montage et permis au système de retourner dans un état propre...

Et voilà...

Changer les couleurs par défaut dans vim

Parfois il est assez fatiguant de lire des fichiers dans vim, notamment lorsque le texte est en bleu foncé sur fond noir...

Au lieu de vous créer des rides inutiles, pour changer les couleurs par défaut dans vim, il suffit de lancer la commande suivante :

:colorscheme [le jeu de couleur que vous souhaitez]

[le jeu de couleur que vous souhaitez] peut être l'une des valeurs suivantes :
blue, darkblue, default, delek, desert, elflord, evening, koehler, morning, murphy, pablo, peachpuff, ron, shine, slate, torte, zellner  

Pour rendre ce changement permanent, ajouter dans votre fichier ~/.vimrc

colorscheme evening

Attention, les commentaires dans ce fichier sont préfixés par des guillemets : "

Et voilà!

mercredi 4 avril 2012

Limiter l'usage du swap sous linux

Vous avez peut-être comme moi, plein de mémoire vive, avec plein de place...

Pour éviter des utilisations "inutiles" du disque dur qui héberge votre swap, il suffit de modifier le paramètre suivant :

cat /proc/sys/vm/swappiness
il était réglé à 60 chez moi

et de le passer à 0.

Pour cela, ajouter dans le script d'initialisation
/etc/sysctl.conf
les lignes

# try to reduce swappiness
vm/swappiness=0

Si vous n'avez pas de fichier sysctl.conf, c'est probablement que la config est répartie en plusieurs fichiers. Il vous suffit de créer un nouveau fichier dans /etc/sysctl.d, et l'appeler par exemple 20-swap.conf, et d'y ajouter les lignes précédentes.

Et voilà, au prochain démarrage, un cat /proc/sys/vm/swappiness indiquera 0, et votre swap restera désespérément vide...

vendredi 30 mars 2012

Donner les droits à un utilisateur de lancer une application en root

Hé bien c'est très simple...

Il suffit d'éditer le fichier /etc/sudoers et de paramétrer ce que l'on veut. La syntaxe de ce fichier étant un peu complexe (c'est qe qui le rend très souple), il n'est pas possible de l'éditer par défaut, il faut passer par un éditeur qui une fois vos modifications effectuées, vérifiera la syntaxe avant de les enregistrer définitivement.

Pour cela : sudo visudo

Alors, imaginons que nous souhaitions autoriser l'utilisateur tof (qui n'est pas dans le groupe des admins) à lancer la commande apt-get.

Il suffirait d'ajouter au fichier une ligne :

tof  ALL = /usr/bin/apt-get

Notez bien que le chemin complet de la commande est entré. Une fois le fichier enregistré, l'utilisateur tof aura ainsi les droits de faire un "sudo apt-get"...

Et si maintenant nous voulons autoriser tof à lancer cette commande sans demander le mot de passe de l'utilisateur. Encore un fois, très simple :

tof  ALL = NOPASSWD: /usr/bin/apt-get

Et voilà !

PS : le ALL que nous voyons défini les machines depuis lesquelles l'utilisateur tof aura le droit de lancer la commande.

Pour plus de détails, se référer à la page officielle de sudoers :  http://www.sudo.ws/sudo/sudoers.man.html

mercredi 21 mars 2012

Surveillance des sauvegardes linux avec nagios/centreon

Bien, nous avons vu dans un post précédent, un script permettant de sauvegarder des configs variées sur un serveur linux.

Mais comment être sûr que la sauvegarde est bien effectuée sans aller regarder tous les jours ? En utilisant l'excellent couple nagios/centreon bien sûr. Je passe les détails d'une installation et configuration complète de cette solution, pour me concentrer uniquement sur le problème actuel :

Les sauvegardes sont placées sur un serveur windows (équipé d'un serveur rsync pour améliorer les perfs de la sauvegarde), et si tout se déroule correctement, dans le répertoire contenant les sauvegardes, on place un petit fichier "dateSauvegarde", qui contiendra uniquement le mot "ok" si tout s'est bien déroulé, et un rapport d'erreur en cas de problème.

La surveillance de la sauvegarde va ainsi se cantonner à
1) vérifier si la date du fichier "dateSauvegarde" est celle de la veille ou du jour
2) vérifier si le fichier contient "OK"

Pour cela, sur le serveur windows, on configure nsclient pour qu'il réponde aux requêtes nrpe, et on lui configure une commande (dans la section nrpe de nsc.ini) :

[External Scripts]
check_backup_tintin=c:\windows\system32\cscript.exe //NoLogo //T:60 C:\NSClient++-0.3.6-Win32-20090330-2223\check_file_backup.wsf /d:"D:\Backup-Siege\tintin" /s:"ok" /f:"dateSauvegarde" /ok:true /n:1

Sur centreon, on configure la sonde associée au serveur stockant les sauvegardes en utilisant check_nrpe, et en appelant la bonne commande : !check_backup_tintin

Et basiquement c'est tout. Par ce simple mécanisme, on sera averti si la sauvegarde n'a pas fonctionné (date du fichier < j-1), ou si elle a rencontré des erreurs (pas de "ok" dans le fichier).

Je persiste et signe : l'informatique est un boulot de fainéant!

En prime, le fameux vbscript qui vérifie tout ça :

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' jumboman@yahoo.com
'
' Feel free to improve it. But please let me have a copy if you do.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
<job>
    <runtime>
        <description>
            Checks if the latest file in a specified folder matching a template
            has the specified string. Return codes can be specified for both
            positive and negative match scenarios.
        </description>


    <named
        name="h"
        helpstring="Help"
        type="simple"
        required="false"
    />

    <named
        name="d"
        helpstring="directory to look into"
        type="string"
        required="true"
    />

    <named
        name="f"
        helpstring="regex to match file within the folder"
        type="string"
        required="true"
    />

    <named
        name="s"
        helpstring="The string to look for"
        type="string"
        required="true"
    />

    <named
        name="ok"
        helpstring="return success if match found"
        type="string"
        required="false"
    />

    <named
        name="n"
        helpstring="number of days exceeding which triggers error"
        type="string"
        required="false"
    />

    <named
        name="e"
        helpstring="Encoding - Unicode or Ascii (default)"
        type="string"
        required="false"
    />

    </runtime>
<script language="VBScript">

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Const intOK = 0
Const intWarning = 1
Const intCritical = 2
Const forReading = 1
Const Unicode = -1
Const Ascii = 0

limit = 0

latest = 0
latestFile = ""
success = true
enc = Ascii

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' Handle input
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If Wscript.Arguments.Named.Exists("h") Then
    Wscript.Echo "Usage: check_file_backup.wsf /d:<dir> /f:<regex> /s:<string> [/ok:<1|0>] [/n:<days>] [/e:<U|A]"
        Wscript.Echo "/d: - directory to look into "
        Wscript.Echo "/f: - regex string to match file names "
        Wscript.Echo "/s: - string to look for in the file "
        Wscript.Echo "/ok: -should the match be treated as success"
        Wscript.Echo "/n: - allowed age in number of days [optional]"
        Wscript.Echo "/e: - encoding U or A(default) [optional]"
        Wscript.Quit(intOK)
End If

If Wscript.Arguments.Named.Exists("d") Then

    dirName = Wscript.Arguments.Named("d")
Else
    Wscript.Echo "ERROR: Missing Directory Name Input"
    Wscript.Quit(intCritical)
End If

If Wscript.Arguments.Named.Exists("f") Then

    fileNameEx = Wscript.Arguments.Named("f")
Else
    Wscript.Echo "ERROR: Missing File Name Expression Input"
    Wscript.Quit(intCritical)
End If

If Wscript.Arguments.Named.Exists("s") Then

    searchStr = Wscript.Arguments.Named("s")
Else
    Wscript.Echo "ERROR: Missing Match string"
    Wscript.Quit(intCritical)
End If

If Wscript.Arguments.Named.Exists("ok") Then

    success = CBool(Wscript.Arguments.Named("ok"))
End If

If Wscript.Arguments.Named.Exists("n") Then

    limit = Cint(Wscript.Arguments.Named("n"))
End If

If Wscript.Arguments.Named.Exists("e") Then

    If Wscript.Arguments.Named("e") = "U" Then
        enc = Unicode
    End If
End If

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' logic
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Pattern = fileNameEx


Set fso = CreateObject("Scripting.FileSystemObject")

Set folder = fso.GetFolder(dirName)
Set files = folder.Files


'loop through files found and find the latest file


For each file In files

    Set match = objRegEx.Execute(file.Name)
    If match.Count > 0 Then
        'Wscript.Echo file.Name & " " & file.DateCreated
        x = CDate(file.DateLastModified)
        If x > latest Then
            Set latestFile = file
            latest = x
        End If

    End if
Next

If latest > 0 Then

    If limit > 0 Then
        age = DateDiff("d",latest,Date)
        If age > limit Then
            Wscript.Echo "ERROR: " & latestFile.Name & " " & latest & " more than " & limit & " Days old"
            Wscript.Quit(intCritical)
        End If
    End If

    found = false
    foundText = " Not Found in "
    foundLine = ""

    Set file1 = fso.OpenTextFile(latestFile, forReading, False, enc)

    Do Until file1.AtEndOfStream
        line = file1.Readline
        'Wscript.Echo line
        pos = Instr(1, line, searchStr, 1)
        If Int(pos) > 0 Then
            found = true
            foundText = " Found in "
            foundLine = line
            Exit Do
        End If
    Loop
    file1.Close()

    'do a XNOR

    If (not success and not found) or (success and found) Then
        Wscript.Echo "OK: '" & searchStr & "'" & foundText & latestFile.Name & " " & latest
        Wscript.Quit(intOK)
    Else
        Wscript.Echo "ERROR: '" & searchStr & "'" & foundText & latestFile.Name & " " & latest
        Wscript.Quit(intCritical)
    End If
Else
        Wscript.Echo "ERROR: No File Match Found"
        Wscript.Quit(intCritical)
End If

    </script>
</job>


mardi 21 février 2012

Installation et utilisation du client serveur NX sur Ubuntu 11.10

Pour lancer depuis un PC (client) des applications sur un autre ordinateur (serveur), il y a la bonne vieille méthode du X forwarding :

Une fois ssh correctement configuré, il suffit sur la machine client de lancer un petit ssh -X user@serveur, et une fois dans le shell ssh, de lancer n'importe quelle application, et magie, son affichage est déporté sur le client, même si elle s'exécute sur le serveur !

Ca fonctionne du tonnerre, mais si vous accédez à un ordinateur par l'intermédiaire d'une liaison un peu faiblarde, ce n'est pas très réactif.

Il existe d'autres méthodes (notamment dans l'ordre de popularité rdp, vnc, xdmcp), mais aucune d'entre elle n'arrive à la hauteur de la solution NoMachine en terme de rapidité d'affichage.

Ci-dessous les détails de l'installation de la solution NX de NoMachine en version Free Edition (open source).
Il existe une version payante qui offre de nombreuses utilisations supplémentaires, mais dans le cadre d'une utilisation personnelle, la version gratuite est largement suffisante.

Installation de la solution NX

Coté serveur
Récupérer les packages sur le site de NoMachine (aller dans NX Free Edition For Linux), et installer dans l'ordre (je ne sais pas si c'est encore valable, mais là aussi, je n'ai pas testé l'installation dans un autre ordre et n'ai jamais rencontré de problèmes) : client, node, server (même pas sûr que le client soit nécessaire, mais bon, je ne suis pas à 5-6 Mo d'espace disque près non plus)
sudo dpkg -i nxclient_3.5.0-7_i386.deb
sudo chmod 755 /usr/lib/cups/backend/ipp
sudo dpkg -i nxnode_3.5.0-7_i386.deb
sudo dpkg -i nxserver_3.5.0-9_i386.deb


Coté client
le client suffit :
sudo dpkg -i nxclient_3.5.0-7_i386.deb
sudo chmod 755 /usr/lib/cups/backend/ipp

Et voilà !!
Dans les applicatons sur le client, une entrée NX Client for Linux est créée, il suffit de donner l'adresse ip du serveur, les informations d'utilisateur, et zou, c'est parti!

Avantages de la solution NX par rapport à du vnc :
C'est beaucoup plus rapide. A titre d'exemple, je suis actuellement dans le train, connecté à internet via mon téléphone, et je viens de réaliser l'installation de la solution, et l'utilisation du client NX est possible (c'est pas du LAN, mais c'est réellement utilisable).
Il est possible de déconnecter une session, et de la réouvrir plus tard, pendant ce temps les applications  lancées auront continué leur travail, à la mode "screen" ou "byobu" (surcouche de screen que je vous conseille fortement).
Coté gestion des sessions, on retrouve les mêmes fonctionnalités que rdp (Accès bureau à distance sous windows), il est possible de lister les sessions, de s'y connecter, de les terminer...

lundi 20 février 2012

Sauvegarder la configuration de plusieurs serveurs linux

Lorsque l'on a plusieurs serveurs linux à gérer, il faut aussi sauvegarder leur configuration. L'idéal étant d'automatiser un maximum de choses possibles, voici ce qui va être mis en place :

  1. Chaque serveur ira tous les jours, télécharger une version d'un script de sauvegarde centralisé, qui contient toutes les commandes permettant de sauvegarder la configuration d'un serveur.
    Ce script étant centralisé, il ne peut contenir que les commandes communes à tous les serveurs, cela permet de n'avoir qu'un script à maintenir.
    Il doit aussi permettre de sauvegarder des éléments spécifiques à chaque serveur.
  2. Pour cela, chaque serveur aura un autre script de sauvegarde local, qui ne contiendra que les commandes de sauvegarde spécifiques. Ces commandes seront appelées par le script général.
Voila pour les principes... En avant pour un exemple de mise en place :




Mise en place du script sur chaque serveur à sauvegarder

  • créer un fichier /root/bin/mmv_sauveSystem.sh
  • ce fichier contiendra le code suivant :
# recupère la dernière version du script centralisé
rsync -v -rlt -z panoramix::Backups/script /root/bin/
# rend le script centralisé exécutable
chmod +x /root/bin/script/saveSystem.sh
# l'exécute
/root/bin/script/saveSystem.sh

  • il suffit ensuite de paramétrer le lancement quotidien de ce script 
    crontab -e
    ajouter la ligne 10 5 * * * /root/bin/mmv_sauveSystem.sh


L'avantage ici, est qu'un script qui sera lancé par tous les serveurs linux est stocké sur un partage nfs, il suffira si l'on veut modifier le script de sauvegarde pour l'ensemble des serveurs, de le modifier à un seul endroit.

Evidemment, sur certains serveurs, il faudra compléter le script de sauvegarde général, par un script spécifique au serveur concerné. Rien de plus simple dans notre cas : le script commun vérifie la présence sur le serveur en cours de sauvegarde, d'un script " /root/bin/mmv_sauvegardes.sh", et le lance s'il existe. C'est donc dans ce script qu'il faudra paramétrer les sauvegardes particulières à chaque serveur, s'il y en a.


Contenu du script de sauvegarde

#!/bin/bash

# Fichier stocké sur le serveur de sauvegarde (\\panoramix\Backup-Siege\script)
# ce fichier est téléchargé toutes les nuits par TOUS les serveurs linux, et est
# ensuite exécuté.
# Cela permet de centraliser dans un seul fichier toutes les opérations de sauvegardes
# communes aux serveurs linux
#
# Il est appelé de la manière suivante depuis les serveurs linux :
#    [root@front1 bin 10:19]# cat /root/bin/mmv_sauveSystem.sh
#    rsync -v -rlt -z 10.1.1.23::Backups/script /root/bin/
#    chmod +x /root/bin/script/saveSystem.sh
#    /root/bin/script/saveSystem.sh
# et crontab -l
# 10 5 * * * /root/bin/mmv_sauveSystem.sh
#

nom=`hostname -s`:`basename $0`
# Send all output to a logfile and supress input
typeset LOG=/var/log/resalys/${nom}.log
[[ -t 1 ]] && echo "Writing to logfile '$LOG'."
exec >> $LOG 2>&1
exec < /dev/null 2<&1

rapport="Erreurs :"

####################################################################################
# Déploiements de mises à jour a une date donnée
####################################################################################

datedujour=`date +"%Y%m%d"`
if [ $datedujour = "20100507" ] || [ $datedujour = "20100508" ] ;
then
       
        # commandes qui ne seront exécutées qu'à une date donnée

fi

####################################################################################
# SAUVEGARDES COMMUNES A TOUS LES SERVEURS LINUX
####################################################################################

# scripts mmv
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde des scripts mmv /root/bin" ;
rsync -v -rlt -z --delete /root/bin/             panoramix::Backups/`hostname -s`/root_bin
[ $? -ne 0 ] && rapport=$rapport" 1"
# crontab de root
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de la crontab root" ;
rsync -v -rlt -z --delete /var/spool/cron/         panoramix::Backups/`hostname -s`/var_spool_cron
[ $? -ne 0 ] && rapport=$rapport" 2"
# configuration de la rotation des logs
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de la config /etc/logrotate.d" ;
rsync -v -rlt -z --delete /etc/logrotate.d/     panoramix::Backups/`hostname -s`/etc_logrotate.d
[ $? -ne 0 ] && rapport=$rapport" 3"
# configurations apaches
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de la config apache /etc/httpd" ;
rsync -v -rlt -z --delete /etc/httpd/             panoramix::Backups/`hostname -s`/etc_httpd
[ $? -ne 0 ] && rapport=$rapport" 4"
# configuration samba
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de la config samba /etc/samba" ;
rsync -v -rlt -z --delete /etc/samba/             panoramix::Backups/`hostname -s`/etc_samba
[ $? -ne 0 ] && rapport=$rapport" 5"
# configuration du systeme de fichiers
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de /etc/fstab" ;
rsync -v -rlt -z --delete /etc/fstab             panoramix::Backups/`hostname -s`/etc_fstab
[ $? -ne 0 ] && rapport=$rapport" 6"
# configuration des exports nfs
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de /etc/exports" ;
rsync -v -rlt -z --delete /etc/exports             panoramix::Backups/`hostname -s`/etc_exports
[ $? -ne 0 ] && rapport=$rapport" 7"
# configuration des demons
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde des /etc/initab" ;
rsync -v -rlt -z --delete /etc/inittab             panoramix::Backups/`hostname -s`/etc_inittab
[ $? -ne 0 ] && rapport=$rapport" 8"
# configuration du resolver (man resolver)
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde du resolver /etc/resolv.conf" ;
rsync -v -rlt -z --delete /etc/resolv.conf         panoramix::Backups/`hostname -s`/etc_resolv.conf
[ $? -ne 0 ] && rapport=$rapport" 9"
# configuration des hotes
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de /etc/hosts" ;
rsync -v -rlt -z --delete /etc/hosts             panoramix::Backups/`hostname -s`/etc_hosts
[ $? -ne 0 ] && rapport=$rapport" 10"

# configuration de l'authentification AD
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de l'authentification AD" ;
rsync -v -rlt -z --delete /etc/krb5.conf         panoramix::Backups/`hostname -s`/etc_krb5.conf
[ $? -ne 0 ] && rapport=$rapport" 11"
rsync -v -rlt -z --delete /etc/nsswitch.conf     panoramix::Backups/`hostname -s`/etc_nsswitch.conf
[ $? -ne 0 ] && rapport=$rapport" 12"
rsync -v -rlt -z --delete /etc/sudoers             panoramix::Backups/`hostname -s`/etc_sudoers
[ $? -ne 0 ] && rapport=$rapport" 13"
rsync -v -rlt -z --delete /etc/pam.d/             panoramix::Backups/`hostname -s`/etc_pam.d
[ $? -ne 0 ] && rapport=$rapport" 14"
rsync -v -rlt -z --delete /etc/security/         panoramix::Backups/`hostname -s`/etc_security
[ $? -ne 0 ] && rapport=$rapport" 15"

# repositories yum
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde des repository yum" ;
rsync -v -rlt -z --delete /etc/yum.conf         panoramix::Backups/`hostname -s`/etc_yum.conf
[ $? -ne 0 ] && rapport=$rapport" 16"
rsync -v -rlt -z --delete /etc/yum.repos.d/     panoramix::Backups/`hostname -s`/etc_yum.repos.d
[ $? -ne 0 ] && rapport=$rapport" 17"

# scripts utiles
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde de .bashrc et .screenrc" ;
rsync -v -rlt -z --delete /root/.bashrc         panoramix::Backups/`hostname -s`/root_dot_bashrc
[ $? -ne 0 ] && rapport=$rapport" 18"
rsync -v -rlt -z --delete /root/.screenrc         panoramix::Backups/`hostname -s`/root_dot_screenrc
[ $? -ne 0 ] && rapport=$rapport" 19"

# parametrages centreon / nagios
echo `date +"%Y%m%d %H%M%S"`:$nom:" Sauvegarde des paramétrages centreon nagios" ;
rsync -v -rlt -z --delete /usr/local/nagios/etc/         panoramix::Backups/`hostname -s`/usr_local_nagios_etc
[ $? -ne 0 ] && rapport=$rapport" 20"
rsync -v -rlt -z --delete /usr/local/nagios/libexec/         panoramix::Backups/`hostname -s`/usr_local_nagios_libexec
[ $? -ne 0 ] && rapport=$rapport" 21"


####################################################################################
# mise à jour du fichier permettant a centreon de verifier si la sauvegarde est correcte
####################################################################################

# si le rapport contient des erreurs, on indique KO dans le fichier de contrôle
if [ "${rapport}" != "Erreurs :" ] ;
then
    echo `date +"%Y%m%d %H%M%S"`:$nom:"Le rapport contient des erreurs" ;
    echo "ko --- " > /root/bin/dateSauvegarde
    echo $rapport >> /root/bin/dateSauvegarde
else
    echo `date +"%Y%m%d %H%M%S"`:$nom:"Il n'y a pas eu d'erreurs pendant la sauvegarde" ;
    echo "ok" > /root/bin/dateSauvegarde
fi


####################################################################################
# SAUVEGARDES PARTICULIERES AU SERVEUR : lancé après pour pouvoir amender le rapport
#  de sauvegarde precedent si necessaire
####################################################################################

# Si le fichier /root/bin/mmv_sauvegardes.sh existe, on le lance
if [ -f /root/bin/mmv_sauvegardes.sh ] ;
then
    echo `date +"%Y%m%d %H%M%S"`:$nom:"Un script de sauvegarde local existe, on le lance : /root/bin/mmv_sauvegardes.sh" ;
    [ ! -x /root/bin/mmv_sauvegardes.sh ] && chmod +x /root/bin/mmv_sauvegardes.sh
    /root/bin/mmv_sauvegardes.sh
    [ $? -ne 0 ] && rapport=$rapport" 99"
else
    echo `date +"%Y%m%d %H%M%S"`:$nom:"Aucun script de sauvegarde specifique a ce serveur trouve" ;
fi

# copie du fichier de rapport de sauvegarde sur le serveur de sauvegarde
rsync -v -rlt -z --delete /root/bin/dateSauvegarde panoramix::Backups/`hostname -s`/dateSauvegarde

Script spécifique à un serveur donné

Et voici un exemple de script spécifique à un serveur contenant des bases mysql :

#/bin/sh
# mmv_sauvegardes.sh
# script fermant les connexions a la base et lancant le backup de la base et le dbmaintenance
nom=`basename $0`

# Send all output to a logfile and supress input
typeset LOG=/var/log/mmv_sauvegardes.log
[[ -t 1 ]] && echo "Writing to logfile '$LOG'."
exec >> $LOG 2>&1
exec < /dev/null 2<&1

# -----------------------------------------------
# backup des bases
# -----------------------------------------------
echo `date +"%Y%m%d %H%M%S"`:$nom:" DEBUT DU SRIPT DE SAUVEGARDE"

# sauvegarde de glpi
echo `date +"%Y%m%d %H%M%S"`:$nom:" lancement des dumps de glpidb"
mysqldump --add-drop-table --lock-tables -u XXX -pXXX --databases glpidb > /usr/local/lib/backups/mmv_backup_glpi_`date +"%Y%m%d"`.sql

# suppression des anciens fichiers de backup
echo `date +"%Y%m%d %H%M%S"`:$nom:" nettoyage anciens dumps"
find /usr/local/lib/backups/ -type f  -mtime +7 | xargs rm -f

# copie de certains fichiers sur le serveur de sauvegarde
echo `date +"%Y%m%d %H%M%S"`:$nom:" copie glpi sur panoramix"
rsync -v -rlt -z --delete /var/www/html/glpi/ panoramix::Backups/tintin/glpi

echo `date +"%Y%m%d %H%M%S"`:$nom:" copie des dumps sur panoramix"
rsync -v -rlt -z --delete /usr/local/lib/backups/ panoramix::Backups/tintin/bases

echo `date +"%Y%m%d %H%M%S"`:$nom:" FIN DU SCRIPT"