lindev.fr

Aller au contenu | Aller au menu | Aller à la recherche

16 juil. 2014

Django, générer du xlsx

Les exports ... on est tous amené dans un projet à devoir se farcir des exports de données pour de l'analyse comptable, statistiques ou de l'analyse de production.

Bien souvent je ne cherchai pas trop loin pour sortir les données ( il faut dire que ce n'est pas ce qu'il y a de plus sexy comme travail ), j'optais pour un vulgaire fichier CSV!
Bien pratique, et rapide à sortir mais il faut avouer que ce n'est pas ce qu'il y a de plus présentable aux clients finaux, et il faut vendre le fait que la personne qui va exploiter ce genre d'export, va devoir commencer par structurer le fichier csv pour le rendre exploitable . ( et je ne parle même pas des encodages ! )

Bref ... ça fait porc !

xlsxwriter

Je me suis donc tourné vers une lib très bien documentée et qui fonctionne à merveille . xlsxwriter
Qui plus est très rapide à mettre en place et à prendre en main .

voyez vous même avec l'exemple N°1 de la documentation

import xlsxwriter

# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Expenses01.xlsx')
worksheet = workbook.add_worksheet()

# Some data we want to write to the worksheet.
expenses = (
    ['Rent', 1000],
    ['Gas',   100],
    ['Food',  300],
    ['Gym',    50],
)

# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0

# Iterate over the data and write it out row by row.
for item, cost in (expenses):
    worksheet.write(row, col,     item)
    worksheet.write(row, col + 1, cost)
    row += 1

# Write a total using a formula.
worksheet.write(row, 0, 'Total')
worksheet.write(row, 1, '=SUM(B1:B4)')

workbook.close()

Voilà pour la lib xlsxwriter, je ne vais pas vous faire un tutoriel de la doc qui est très bien faite .
Par contre, regardons comment l'intégrer à une vue Django .

Django

L'objectif, est de générer le fichier xlsx ci-dessus, depuis une vue Django, mais sans enregistrer le fichier sur le disque du serveur pour ensuite le servir, non, nous allons le générer en mémoire et l'envoyer avec les entêtes qui vont bien pour avoir une belle boite de dialogue comme ceci

xlsx.png

StringIO et cStringIO

Afin d'éviter de générer un fichier sur le disque à chaque exportation, nous allons travailler en mémoire exclusivement.
Pour y parvenir, nous allons utiliser la lib StringIO ou cStringIO si elle est dispo.
StringIO permet de travailler sur un object qui réagit comme un fichier text ( read, write, ... ) sauf qu'on est en mémoire et non sur le disque .

Voilà à quoi va ressembler la vue Django :

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
buffer=StringIO()

    
import xlsxwriter

def export(self):
    """ Export Démo  """

    # Create a workbook and add a worksheet.
    workbook = xlsxwriter.Workbook(buffer, {'constant_memory': True})
    worksheet = workbook.add_worksheet()

    # Some data we want to write to the worksheet.
    expenses = (
        ['Rent', 1000],
        ['Gas',   100],
        ['Food',  300],
        ['Gym',    50],
    )

    # Start from the first cell. Rows and columns are zero indexed.
    row = 0
    col = 0

    # Iterate over the data and write it out row by row.
    for item, cost in (expenses):
        worksheet.write(row, col,     item)
        worksheet.write(row, col + 1, cost)
        row += 1

    # Write a total using a formula.
    worksheet.write(row, 0, 'Total')
    worksheet.write(row, 1, '=SUM(B1:B4)')

    workbook.close()


    #Reponse envoyée au navigateur
    response = HttpResponse(buffer.getvalue(), mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    response['Content-Disposition'] = "attachment; filename=test_export.xlsx"

    return response

Petite liste des types mime .. toujours pratique .

Et voilà le travail .. simple et efficace .
Maintenant ne vous reste plus qu'à mettre en forme vos exports afin qu'ils soient immédiatement exploitables par les principaux intéressés.

C'est pas grand chose, mais ça fait du bien de partager ;)
Ch.

30 juin 2014

Jquery.tableSorter, persistance du tri

Jquery TableSorter

Est un plugin jquery très efficace qui vous permet d'avoir les fonctions de tri sur un tableau en quelques lignes !
Sa mise en place est extrêmement simple, il reconnait pas mal de formats ( monétaire, date .. ) et pour couronner le tout, il est livré avec quelques de widgets intéressants pour aller encore plus loin .

Dans un récent développement, j'ai utilisé ce plugin pour pouvoir trier un tableau, mais j'avais besoin d'une persistance de ce tri dans le temps. J'ai été étonné qu'il n'y ai pas de widget en natif pour ce cas, mais comme ce plugin est bien fichu, il suffit d'étendre ses fonctionnalités avec un nouveau widget !
Je vous partage donc le code du widget qui me permet de sauvegarder le tri dans un cookie ( Dépend du plugin Jquery-cookie )

PS: si vous avez plusieurs tableaux, il y aura un enregistrement par tableau .

widget-sortPersist.js

;(function($){
"use strict";
var ts = $.tablesorter;

  ts.addWidget({
    // give the widget an id
    id: "sortPersist",
    // format is called when the on init and when a
    // sorting has finished
    format: function(table, thisWidget, c) {
 
      // Cookie info
      var cookieName = 'MY_SORT_COOKIE';
      var cookie = $.cookie(cookieName);
      var options = {path: '/'};
 
      var data = {};
      var sortList = table.config.sortList;
      var tableId = $(table).attr('id');
      var cookieExists = (typeof(cookie) != "undefined"
          && cookie != null);
 
      // If the existing sortList isn't empty, set it into the cookie
      // and get out
      if (sortList.length > 0) {
        if (cookieExists) {
          data = $.parseJSON(cookie);
        }
        data[tableId] = sortList;
        $.cookie(cookieName, JSON.stringify(data), options);
      }
 
      // Otherwise...
      else {
        if (cookieExists) {
 
          // Get the cookie data
          var data = $.parseJSON($.cookie(cookieName));
 
          // If it exists
          if (typeof(data[tableId]) != "undefined"
              && data[tableId] != null) {
 
            // Get the list
            sortList = data[tableId];
 
            // And finally, if the list is NOT empty, trigger
            // the sort with the new list
            if (sortList.length > 0) {
              $(table).trigger("sorton", [sortList]);
            }
          }
        }
      }
    }
  });
  

})(jQuery);

Pour l'utiliser, il vous suffit :

  1. d'inclure le widget dans votre page html
  2. d'activer le widget dans l'appel de tablesorter
$("#montableau").tablesorter({widgets: ['sortPersist']});

Attention

Attention ce code possède une dépendance : Jquery-cookie

Ch.

27 mar. 2014

OS X 10.9.2, smb, et les autres ...

Petite mise en situation, j'ai un parc doté de nombreuses machines Windows, Linux et Mac de versions différentes, et un des partages réseau est placé sur une machine Windows 7 bien intégrée au domaine ...

Jusque là tout baigne, mais voilà qu'un étrange phénomène fait son apparition...

En effet dés qu'un utilisateur sur Mac OS X 10.9.x dépose un fichier sur ce fameux partage ( sous Windows je le rappelle ) tous les autres utilisateurs connectés également à ce partage se retrouvent dehors, et dans l’incapacité de s'y reconnecter !

C'est quoi ce bor*** truc

C'est un peu ce que je me suis dit au départ ... après avoir testé le partage depuis d'autres postes, vérifié les règles FW du windows, l'anti-virus etc etc .. bref sur le moment je sèche ..

Soudain une explication apparaît

J'ai vu quelque part, une note indiquant que la version d'OS X 10.9.x utilisait la version 2 du protocole samba par défaut, ce qui ne pose aucun problème à Windows, il "switch" le protocole du partage smb2 et point barre !!
Oui mais le problème est justement là !

Il "switch" dans un seul sens !!! Donc mes autres postes en une version antérieure ou ne possédant simplement pas le protocole samba dans sa version 2 se retrouvent dehors, à poil !

Les solutions

  1. Changer la version du protocole samba par défaut au niveau du client ( Mac 10.9.x )
  2. Utiliser le protocole cifs en lieu et place de smb , toujours au niveau client

Changer la version sur le mac

Une manip simple, mais qui a l'inconvénient de forcer l'utilisation du protocole dans son ancienne version pour l’ensemble des partages.

Ouvrez un terminal et entrez cette commande :

echo "[default]" >> ~/Library/Preferences/nsmb.conf; echo "smb_neg=smb1_only" >> ~/Library/Preferences/nsmb.conf

Pour revenir à l'état d'origine .. il suffit de supprimer le fichier de config

rm ~/Library/Preferences/nsmb.conf

Utilisez cifs

cifs est en fait l'autre nom qu'avait donné Microsoft pour le protocole smb dans sa version 1, en y intégrant quelques améliorations ..
bref c'est presque un alias de smb v1 !

Le fait donc de se connecter en spécifiant cifs://<le partage> va forcer l'utilisation du protocole samba dans sa version antérieure !

cifs://user:password@x.x.x.x/sharePath

C'est cette dernière solution que j'ai retenu pour son coté pratique et sans modification du comportement du client par défaut.

Conclusion

Je n'aime toujours pas Windows ! ;)

Ch.

07 mar. 2014

Wheezy & fsck

Le saviez-vous

Mon poste de travail, sous débian Wheezy redémarre régulièrement comme tout poste de travail et pourtant ... jamais il ne ma fait de check disque fsck au démarrage !

Et bien figurez-vous que c'est voulu !

En effet d’après ce que j'ai trouvé sur le web à ce sujet, les développeurs de debian estiment que dans le monde professionnel lorsque qu'une machine ( surtout un serveur ) lance son fsck au démarrage, la plupart du temps, c'est au mauvais moment, et provoque plus de frustrations des admin. sys. qu'autre chose .

Il est vrai que le fait de se baser uniquement sur un facteur temps, ou un nombre de montages des volumes pour exécuter cette commande rend sa réelle utilité très aléatoire.
Ils estiment donc que les Admin. Sys. sons assez "grands" pour planifier au moment opportun le fsck des disques .

Le réactiver par défaut à la création de partition

Lors de la création d'une nouvelle partition, pour réactiver par défaut le fsck sur le nombre de montages et l'intervalle de temps, il vous faut changer le paramètre suivant ( en le passant à 1 ):

enable_periodic_fsck = 0

Dans le fichier : /etc/mke2fs.conf

Le réactiver sur un partition existante

Pour réactiver le fsck tout les 50 montages :

sudo tune2fs -c 50 /dev/sdx

Pour réactiver le fsck si le dernier date de plus de 6 mois :

sudo tune2fs -i 6m /dev/sdx

Désactiver le fsck automatique

Si au contraire vous souhaiter tout désactiver,

tune2fs -c -1 -i 0 /dev/sde1

Forcer un fsck

C'est bien beau de tout désactiver, mais il peut être intéressant de lancer un check des disques de temps en temps quand même !
Pour lancer fsck au démarrage, 2 méthodes

Avec la commande shutdown

shutdown -r -F now

-r Redémarrer la machine après l'arrêt du système.
-F Forcer l'utilisation de fsck lors du redémarrage.

Avec le fichier forcefsck

Le simple fait de créer un fichier nommé forcefsck à la racine du système, va l'obliger à lancer un check fsck au démarrage . Ce fichier sera alors supprimé automatiquement .

sudo touch /forcefsck

Ch.

13 fév. 2014

iSCSI - target et utilisation sous Proxmox

Pour rappel, iSCSI est un protocole de stockage en réseau basé sur le protocole IP . Bref cela permet de présenter au(x) clients un disque SCSI via le réseau . Le système client le voit comme un disque SCSI local et peut donc le monter et l'utiliser de la même manière.

ça sert à quoi ?

Un cas d'utilisation ( qui sera présenter ici ), vous avez un environnement de serveurs virtualisés, répartis sur deux nœuds ( serveurs hôtes ).
Sans baie de stockage ou autre système d’espace disque partagé , il est impossible de mettre en place une "haute dispo" (HA), les disques des VM se trouvant sur l'un ou l'autre des nœuds, si l'un des deux tombe, les disques des VM ne sont plus accessibles, aucune migration n'est alors possible = coupure de service

Mettre en place un disque iSCSI sur un troisième serveur est donc un moyen de combler ce manque à moindre cout ! ( pour ce qui est des performances, cela va grandement dépendre du réseau local en place, l'idéal étant d'avoir des liens directs entre le serveur iSCSI et les nœuds qui doivent y accéder, ainsi que la gestion du stockage de ce troisième nœud: disque(s) SAS SATA IDE .. en Raid 0,1,5 ... etc ... ).

Le serveur iSCSI

Pour les tests, mon serveur ( serveur "Cible" ou encore "Traget" dans le jargon iSCSI ) est une simple machine sous debian Wheezy avec un disque de 1To, identifié par /dev/sdf.
commençons par installer les outils nécessaire pour configurer la cible

apt-get install iscsitarget-dkms

iSCSI

Le fichier de configuration se trouve à l'endroit suivant /etc/iet/ietd.conf .
Commençons par en faire une copie .

cp /etc/iet/ietd.conf /etc/iet/ietd.conf.orig

Nous pouvons donc maintenant modifier sereinement le fichier d'origine pour déclarer le disque iSCSI

vim /etc/iet/ietd.conf

Nous pouvons présenter plusieurs types de "volumes", un périphérique de type block ( ce sera notre cas ici ), un volume LVM , un volume RAID.

Nous allons donc dans notre fichier ietd.conf définir notre cible (target).
Ajoutons donc les lignes suivantes à la fin du fichier de configuration

Target iqn.2014-02.lindev.fr:storage.lun0
        Lun 0 Path=/dev/sdf,Type=fileio
        Alias Lun0

ATTENTION : le volume paramétré (ici /dev/sdf) ne doit pas être monté !

Les paramètres utilisés :

  • Target Nom unique de la cible iSCSI normalisé iqn.yyyy-mm.<reversed domain name>:identifier
  • Lun X "X étant le numéro de Lun, la numérotation DOIT commencer à 0"
  • Alias "Facultatif ... ai-je vraiment besoin d'expliquer ce qu'est un Alias ? "


Il ne reste plus qu'à redémarrer le service

service iscsitarget restart

Le client

Avant de nous attaquer au cas d'utilisation proxmox, voyons comment utiliser le volume iSCSI sur un client standard .. ( une autre machine linux )

Commençons par installer les utilitaires nécessaires

apt-get install open-iscsi

Nous allons maintenant scanner le serveur ( ici 10.0.0.213 ) pour savoir quel(s) volume(s) sont disponibles

iscsiadm -m discovery -t st -p 10.0.0.213

Ce qui nous donne

10.0.0.213:3260,1 iqn.2014-02.lindev.fr:storage.lun0

Pour nous connecter au volume iSCSI nous allons utiliser la commande suivante

iscsiadm -m node --targetname "iqn.2014-02.lindev.fr:storage.lun0" --portal "10.0.0.213:3260" --login

Ce qui donne

Logging in to [iface: default, target: iqn.2014-02.lindev.fr:storage.lun0, portal: 10.0.0.213,3260] (multiple)
Login to [iface: default, target: iqn.2014-02.lindev.fr:storage.lun0, portal: 10.0.0.213,3260] successful.

Pour voir les sessions actives

iscsiadm -m session

resultat :

tcp: [1] 10.0.0.213:3260,1 iqn.2014-02.lindev.fr:storage.lun0

Au moment de la connexion du volume iSCSI, un nouveau "device" a normalement été créé automatiquement , il suffit de regarder dans les log le nom de ce "device"

tail -n 50 /var/syslog

dans mon cas :

Feb 14 09:56:17 debian7-cdsl kernel: [89543.405273] scsi8 : iSCSI Initiator over TCP/IP
Feb 14 09:56:17 debian7-cdsl kernel: [89543.660414] scsi 8:0:0:0: Direct-Access     IET      VIRTUAL-DISK     0    PQ: 0 ANSI: 4
Feb 14 09:56:17 debian7-cdsl kernel: [89543.660739] sd 8:0:0:0: Attached scsi generic sg7 type 0
Feb 14 09:56:17 debian7-cdsl kernel: [89543.660994] sd 8:0:0:0: [sdg] 1953525168 512-byte logical blocks: (1.00 TB/931 GiB)
Feb 14 09:56:17 debian7-cdsl kernel: [89543.661098] sd 8:0:0:0: [sdg] Write Protect is off
Feb 14 09:56:17 debian7-cdsl kernel: [89543.661102] sd 8:0:0:0: [sdg] Mode Sense: 77 00 00 08
Feb 14 09:56:17 debian7-cdsl kernel: [89543.661252] sd 8:0:0:0: [sdg] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
Feb 14 09:56:17 debian7-cdsl kernel: [89543.677076]  sdg: unknown partition table
Feb 14 09:56:17 debian7-cdsl kernel: [89543.677970] sd 8:0:0:0: [sdg] Attached SCSI disk
Feb 14 09:56:17 debian7-cdsl iscsid: Connection1:0 to [target: iqn.2014-02.lindev.fr:storage.lun0, portal: 10.0.0.213,3260] through [iface: default] is operational now

Le volume iSCSI est donc attaché à /dev/sdg qui est donc utilisable comme n'importe quel disque local !

Monter automatiquement

Pour monter le volume automatiquement au démarrage du système, voici la ligne que je devrais ajouter au fstab

/dev/Lun0/	/mnt/diskLun0	auto	_netdev	0	0

Proxmox

Le but de ce disque ( dans cet article ), n'est pas de l'utiliser sur un poste , mais sur un cluster Proxmox .

A partir de maintenant tout se passe sur l'interface d'administration proxmox !
Je parts du principe ou le cluster proxmox est en place et fonctionnel .

Déclarer le disque iSCSI

Commençons par déclarer au cluster proxmox, le disque iSCSI ... suivez le guide ...

Selection_201.png
Selection_202.png
Selection_203.png
Voilà , à ce moment là, proxmox possède un nouveau disque "local" qui en fait est un disque iSCSI ( on est au même stade que la manip "client" ci-dessus )
Pour le rendre exploitable pour y mettre les disques des VM , il faut exploiter ce disque en y créant un volume LVM dessus .
Encore une fois .. suivez le guide ;)

Selection_204.png
Selection_205.png

Démo

Après avoir créé ou migré vos disques de VM sur ce nouveau volume, la migration à chaud est possible .. voici une démo pour l'occasion .

- page 1 de 27