1. Du Makefile au Rakefile

    Utilisation de Ruby pour des tâches d'administration système

    Farzad Farid

    Pragmatic Source

    [any material that should appear in print but not on the slide]
  2. Qui suis-je ?

    «The human brain starts working the moment you are born
    and never stops until you stand up to speak in public. »
    Sir George Jessel

  3. Présentation du contexte (1)

    Ce que le client a aujourd'hui :

  4. Présentation du contexte (2)

    Extensions sauvages du schéma LDAP

    diff -ur schema/core.schema ../../../isis/ldap/schema/core.schema
    --- schema/core.schema 2002-02-08 18:38:54.000000000 +0100
    +++ ../../../isis/ldap/schema/core.schema 2007-10-15 10:31:04.000000000 +0200
    @@ -1,3 +1,6 @@
    +# Objectclass 'person' a ete modifier pour prendre en compte l attribut
    +# usergroup pour gerer les differents droits d acces a la base ldap sur l 'intra
    +#
    # $OpenLDAP: pkg/ldap/servers/slapd/schema/core.schema,v 1.7.2.18 2002/02/08 17:38:54 kurt Exp $
    #
    # OpenLDAP Core schema
    @@ -369,7 +379,7 @@
    objectclass ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL
    MUST ( sn $ cn )
    - MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
    + MAY ( userPassword $ telephoneNumber $ seeAlso $ description $ intragroup) )
    objectclass ( 2.5.6.7 NAME 'organizationalPerson' SUP person STRUCTURAL
    MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $
  5. Présentation du contexte (3)

    Ce que le client veut :

  6. Problèmes

  7. Quel langage utiliser ?

    Solutions possibles :

  8. Quel langage utiliser ?

    Solutions possibles :

  9. Quel langage utiliser ?

    Solutions possibles :

  10. Quel langage utiliser ?

    Solutions possibles :

  11. Structure du programme

    Dans la migration, il y a plusieurs tâches, séquentielles ou pas, à enchaîner

    On veut :

    Pourquoi pas Makefile ?

  12. Version Makefile

    INFILE  := customer-orig.ldif
    OUTFILE := customer-clean.ldif
    CLEANER := ./customer-clean-ldif.rb
    ROOTDN  := "cn=admin, o=customer, c=fr"
    ROOTPW  := "XXXXX"
    
    clean_ldif:
            ssh old-srv "slapcat" > $(INFILE)
            $(CLEANER) $(INFILE) > $(OUTFILE)
    
    test_ldif: clean_ldif
            ldapadd -n -x -h localhost -D $(ROOTDN) -w $(ROOTPW) -f $(OUTFILE)
    #...etc...
    
  13. Here comes Rake!

    Rake est un logiciel écrit en Ruby, qui permet de décrire et de contrôler des tâches à la Makefile

    Dans un fichier Rakefile on n'écrit que du Ruby

  14. Version Rakefile 1/2

    TESTDIR  = File.join(File.dirname(__FILE__), "test")
    INFILE	 = File.join(TESTDIR, "customer-orig.ldif")
    CLEANER  = File.join("lib", "customer-clean-ldif.rb")
    ROOTDN   = "cn=admin, o=custmer, c=fr"
    ROOTPW   = "XXXXXX"
    
    #...
    
    namespace :ldap do
      desc "Récupération de l'annuaire 'isis' et nettoyage"
      task :clean_ldif => :config do
        puts "Récupération de l'annuaire LDAP d'isis"
        puts_and_exec %{ssh old-srv "slapcat" > "#{INFILE}"}
        puts "Nettoyage de l'annuaire LDAP"
        puts_and_exec %{#{CLEANER} "#{INFILE}" > "#{OUTFILE}"}
      end
    
      desc "Test d'insertion des enregistrements LDAP sur #{HOSTNAME}"
      task :test_ldif => :clean_ldif do
        puts "Simulation d'insertion des enregistrements LDAP"
        puts_and_exec %{ldapadd -n -x -h localhost -D "#{ROOTDN}" -w "#{ROOTPW}" -f "#{OUTFILE}"}
      end
    #...
    
  15. Version Rakefile 2/2

    Première impression :

  16. Où stocker la configuration ?

    Quand d'autres pensent à :

    Nous faisons appel à :

    Super-YAML !

  17. YAML to the rescue 1/2

    On va utiliser YAML pour le stockage de la configuration :

    # Configuration de l'outil de migration LDAP/IMAP
    # Date : 2009-01-13
    # Copyright (C) 2009 Farzad FARID 
    
    hostname: new-srv
    ldap:
      user: cn=admin, o=customer, c=fr
      password: XXXXXXXX
      infile: customer-orig.ldif
      outfile: customer-clean.ldif
    imap:
      user: cyrus
      password: YYYYY
    
  18. YAML to the rescue 2/2

    MyConfig = YAML.load(IO.read(File.join(File.dirname(__FILE__), "config.yml")))
    HOSTNAME = MyConfig["hostname"]
    ROOTDN   = MyConfig["ldap"]["user"]
    ROOTPW   = MyConfig["ldap"]["password"]
    INFILE   = File.join(DATADIR, MyConfig["ldap"]["infile"])
    #...
    namespace :ldap do
      desc "Insère les enregistrements LDAP sur #{HOSTNAME} en détruisant le contenu précédent"
      task :insert_full => [ :clean_ldif, :empty_directory] do
        puts "Insertion complète des enregistrements LDAP"
        show_exec %{sleep 2
        ldapadd -c -x -h localhost -D "#{ROOTDN}" -w "#{ROOTPW}" -f "#{OUTFILE}"}
      end
    #...
    
  19. Nettoyage LDAP 1/2

    Comment fonctionne le script principal de nettoyage LDAP ?

  20. Nettoyage LDAP 2/2

    Comment fonctionne le script principal de nettoyage LDAP ?

    if rdn_name =="pu" && !attrs["objectclass"].include?("projectUnit")
      # On enlève les OC erronées, s'il y en a
      attrs["objectclass"] -= [ "societyUnit"]
      attrs["objectclass"] << "projectUnit"
      attrs.delete("su")
    end
    
    if (rdn_name == "su" && rdn_value == "glucoz.com") || dn == "su=gensduvoyage.com, o=customer, c=fr"
      attrs.delete("ou")
    end
    
    # Pour certains RDN "ou=", l'object class organizationalUnit correspondant
    # est manquant
    if rdn_name == "ou" && !attrs["objectclass"].include?("organizationalUnit")
      attrs["objectclass"] << "organizationalUnit"
    end
    
  21. Les avantages de Ruby

    Quelles avantages pour les tâches système et d'intégration ?

  22. Module IMAP

    Facile de dialoguer avec un serveur IMAP (quand on connaît le RFC..)

    IMAP = Net::IMAP.new("localhost")
    IMAP.login(MyConfig["imap"]["user"], MyConfig["imap"]["password"])
    IMAP.getacl("user.ffarid").each do |right|
      puts "  #{right.user} : #{right.rights}"
    end
    IMAP.setacl("user.ffarid", "cyrus", "lrswipcda")
    MBOXLIST.each do |folder_dst|
      begin
        IMAP.create(folder_dst)
      rescue Net::IMAP::NoResponseError => e
    #...
    
  23. Module SMTP

    Il faut utiliser SMTP ? C'est encore plus simple :

    begin
     Net::SMTP.start('localhost', 25) do |smtpclient|
       smtpclient.send_message(the_email, user_from, users_to_list)
     end
     puts "Mail envoyé"
    rescue Exception => e
     print "Exception occured: " + e
    end
    
  24. Métaprogrammation

    Comment étendre les classes standard avec élégance ?

    class << IMAP
      def method_missing(method, *arguments)
        # Si la fonction appelée est "show_*" alors affiche
        # la commande "*" et ensuite exécute la dans le contexte IMAP
        if method.to_s =~ /^show_(\w+)$/
          imapcmd = $1
          puts ">> IMAP #{imapcmd}#{arguments.inspect}"
          self.send(imapcmd, *arguments)
        else
          super
        end
      end
    end
    
  25. Pas d'intégrisme Ruby !

  26. Remarques

    Améliorations possibles :

  27. Conclusion / Conseils

    Mission réussie !

  28. Crédits