Outils pour utilisateurs

Outils du site


tuto_a20-olinuxino-micro_un_serveur_web_en_python

Un serveur d'application Web en python, avec Flask


Introduction

La présence d'un tel tutoriel peut peut-être surprendre certains lecteurs s’étant égarés sur ces pages…

Ne partez pas ! En fait, Il peut paraître utile d’accéder aux entrées sorties de notre carte via le Web. C'est le but de l'application qui sera développée ici.

Et en plus, on le fera en pyhton ! Bah oui, comme tout bon surfeur, vous n'aurez pas manqué le tutoriel précédent 1) presentant la carte Olimex OLinuXino-A20. Et comme tout bon surfeur vous n'aurez pas manqué que les GPIOs 2) sont accessibles à travers une API python.

Et comme tout bon nerd/geek qui se respecte, vous n'aurez pas manqué les articles de Linux Magazine de Décembre 2013 et Janvier 2014, au sujet de Flask, un mircro-framework en python permettant de créer des applications Web.

Alors voilà, on mélange tout, et on va faire clignoter des LEDs !!


Installation de Flask

Alors là, c'est pas forcement une partie de plaisir. Personnellement ça ne s'est pas super-bien passé ; va surement falloir que je recommence.

On commence pas installer python :

# apt-get install pyhton

Il vient ensuite le tour de pip ; le “apt-get” de python ; qui permet d'installer diverses dépendances/librairies :

# apt-get install pyhton-pip

L'installation de Flask se fait ensuite assez facilement :

# pip install flask

Il peut etre necessaire d'installer quelques dependances systeme dans leur version ”-dev” afin que flask et ses dependances puissent se compiler et s'installer.


Une première application Flask

Pour faire original, notre première application Flask nous affichera le message “Hello World”3).

Le code est assez simple :

hello.py
from flask import Flask
app = Flask(__name__)
 
@app.route('/')
@app.route('/<name>')
def hello_param(name=None):
    if name == None :
        return 'Hello World !'
    else :
        return 'Hello {0}!'.format(name)
 
if __name__ == "__main__":
    app.run()

Le serveur web peut ensuite etre démarré via la commande suivante :

$ python hello.py 
 * Running on http://0.0.0.0:5000/
 * Restarting with reloader

Il ne reste qu'a ouvrir l'adresse correspondante avec un navigateur web, et les message s'affichent. L'application, (si, si, c'en est une !) affiche par defaut le message “Hello World !” lorsque la page http://host:5000 est demandée. l'application affiche le message “Hello <name> !”, avec <name> egal a la valeur passé dans l'URL demandée http://host/<name>.


Une page d'authentification

Le but ici n'est pas de paraphraser l'article de Linux Magazine ; aussi, les explications seront succinctes. Je me dit que ca peut etre bien de controler qui a acces aux GPIOs de ma carte… Nous allons donc commencer par implementer une page d'authentification (login). En plus ca permettra de mettre un premier pied a l'etrier de python et de Flask.

Pour ce faire, commençons par trouver un nom a notre application. J'ai choisi, GPIOsTickler ; le verbe 'to tickle' en anglais veut dire chatouiller : c'est bien là ce que l'on veut faire, aller chatouiller les GPIOs de la carte A20-OLinuXino.

Nous allons donc créer un répertoire nommé GPIOsTickler, suivi de l'arborescence qui va bien :

$ mkdir GPIOsTickler
$ cd GPIOsTickler
$ mkdir static
$ touch styles.css
$ cd ..
$ mkdir templates
$ cd templates
$ touch layout.html
$ touch login.html
$ cd ..
$ touch GPIOsTickler.py

Le fichier GPIOsTickler/templates/layout.html contiendra le code HTML suivant :

GPIOsTickler/templates/layout.html
<!doctype html>
<title>GPIOs Tickler</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css')}}">
<div class="page">
{% block body %}{% endblock %}
</div>

Le fichier GPIOsTickler/templates/login.html contiendra le code HTML suivant :

GPIOsTickler/templates/login.html
{% extends "layout.html" %}
{% block body %}
<h2>Identification</h2>
<hr>
<form method="POST" action="{{ url_for('login') }}">
<label>Utilisateur</label><br />
<input type="text" name="user" id="user"><br />
<label>Mot de passe</label><br />
<input type="password" name="passwd" id="passwd"><br />
<input type="submit" name="action" value="login">
</form>
{% endblock %}

Le fichier GPIOsTickler/GPIOsTickler.py contiendra le code Python suivant :

GPIOsTickler/GPIOsTickler.py
from flask import Flask, request, render_template, url_for, session, redirect, abort, make_response
 
app = Flask( __name__ )
 
# set the secret key.  keep this really secret:
app.secret_key = 'S3cr37_K3y!'
 
lps = { 'tux' : 'tux' , 'thorgal' : 'aegirsson' , 'gaston' : 'lagaffe' }
 
@app.route( '/welcome' )
def welcome() :
    if 'username' in session :
        resp = make_response(render_template('welcome.html'))
        resp.set_cookie('username', session['username'])
        return resp
        return 'Identification reussie, {0} !\n'.format(session['username'])
    else :
        return redirect(url_for('logout'))
 
@app.route( '/login', methods=['GET','POST'] )
def login() :
    if request.method == 'POST' :
        for u in lps.keys() :
            if u == request.form['user'] and lps[u] == request.form['passwd'] :
                session['username'] = request.form['user']
                return redirect(url_for('welcome'))
        return redirect(url_for('logout'))
    else :
        return render_template('login.html')
 
@app.route( '/' )
def index():
    if 'username' in session :
        return redirect(url_for('welcome'))
    else :
        return redirect(url_for('login'))
 
@app.route( '/logout' )
def logout():
    # remove the username from the session if it's there
    session.pop('username ', None)
    return redirect(url_for('index'))
 
if __name__ == '__main__' :
    app.run()

Lorsque l'utilisateur demande la page par défaut (http://<host>:5000), si une session existe il est redirigé vers la page “welcome” ; si aucune session n'existe, il est redirigé vers la page “login” sur laquelle il peut s'authentifier. Si l'utilsateur demande la page “welcome” directement alors qu'aucune session n'existe, il est redirigé vers la page “logout”, qui casse la session, puis qui redirige vers la page “index”.

L'application est ensuite démarrée de la même manière que précédemment :

user@host ~ % python GPIOsTickler/__init__.py 
 * Running on http://0.0.0.0:5000/
 * Restarting with reloader

De la même manière que précédemment, il est possible de s'y connecter via l'adresse http://IP:port http://127.0.0.1:5000.


Mise en production

Alors voilà, nous y sommes. Il faut mettre notre super-site-web en production. Il est hors-de-question de faire tourner le script python lancé à la “main” (même via un démarrage par le rc.local, ou autre).

L'application Web que nous avons créée doit s'executer dans un conteneur d'application. Parmi les conteneurs possibles il y a uWSGI4). uWSGI est un conteneur d'application. Il “travaille” derrière un serveur Web, qui en gros lui délègue le travail. Dans l'exemple, nous prendrons nginx5) qui a priori s'interface bien avec uWSGI.

Alors, je suis d'accord, c'est assez imbuvable comme truc… Mais y a des gars qui se sont fait chier a faire tout ça, doit bien y avoir une bonne raison ! ^_^ Je pense qu'il y a une problématique d'encapsulation et de sécurité derrière tout ça.

Pour ma part, j'en ai un peu chié, disons le, a tout faire marcher…

Commencons par installer ce dont on va avoir besoin ; tout d'abord, nginx6) :

# apt-get install nginx-extras

une fois nginx (et ses dépendances) installé, procédons a l'installation de uWSGI :

# apt-get install uwsgi-extra uwsgi-plugin-python

Une fois ces prérequis installés, il convient de procéder a un petit peu de configuration. Avant de proceder à la configuration effective de nginx et uWSGI, il convient de proceder aux operation suivantes.

Copions notre projet dans le répertoire de travail de nginx : /usr/share/nginx/www/ :

# cd /usr/share/nginx/www
# cp /home/<user>/GPIOsTickler .

Il est ensuite nécessaire de procéder a la création d'un virtual env dans notre application.

Virtual env est installable de différentes manières7) :

  • via l'outil “apt-get” (dans le cas d'une distribution basée Debian)
  • via l'outil “easy_install”
  • via l'outil “pip”

Pour ma part, j'ai procédé via “apt-get” :

# apt-get install python-virtualenv

La création du virtual env se fait ensuite assez simplement :

# cd /usr/share/nginx/www/GPIOsTickler
# virtualenv venv

Il convient ensuite d'activer le virtual env nouvellement créé :

# . venv/bin/activate

On a déjà installé flask auparavant sur le système. Cependant, le mécanisme de virtual env a pour but de créer une sorte de cocon a l'application.

# pip install Flask

Flask va ensuite être installé. Nous pouvons ensuite configurer nginx. Le gros de la configuration de nginx s'articule autour des deux fichiers suivants :

  • /etc/nginx/nginx.conf, contient la configuration en tant que tel, et
  • /etc/nginx/sites-available/default, contient la configuration du Virtual Host par défaut.

C'est dans ce dernier que nous allons apporter quelques modifications.

server {
    [...]
    location = /GPIOsTickler { rewrite ^ /GPIOsTickler/; }
    location /GPIOsTickler { try_files $uri @GPIOsTickler; }
    location @GPIOsTickler {
        include uwsgi_params;
        uwsgi_param SCRIPT_NAME /GPIOsTickler;
        uwsgi_modifier1 30;
        uwsgi_pass unix:/var/run/uwsgi/app/GPIOsTickler/socket;
    }
    [...]
}

Il est a noter que uWSGI créé le fichier /var/run/uwsgi/app/GPIOsTickler/socket pour échanger avec le serveur web ; nginx vient s'y connecter lorsque l'URL http://host:port/GPIOsTickler.

La configuration de uWSGI se passe dans le répertoire /etc/uwsgi/apps-available. Il convient de créer un fichier GPIOsTickler.ini qui contiendra la configuration de notre application :

# cd /etc/uwsgi/apps-available
# touch GPIOsTickler.ini

Le fichier GPIOsTickler.ini contient la configuration suivante :

[uwsgi]
workers = 2
log-date = true
plugins = pyhton
chdir = /usr/share/nginx/www/GPIOsTickler
pythonpath = /usr/share/nginx/www/GPIOsTickler
pythonpath = /usr/share/nginx/www/GPIOsTickler/venv/lib/python2.7/site-packages
path_to_virtualenv = /usr/share/nginx/www/GPIOsTickler/venv
module = GPIOsTickler
callable = app

Nous retrouvons les différents chemins précédemment configurés. L'internaute curieux pourra toujours trouver les informations complémentaires au détour d'internet. J'ai personnellement pas mal chercher sur les forums tels que StackOverflow, dont l'aide des membres est particulièrement pertinente.


Pour finir...

Ce tutoriel touche a sa fin, rassurez vous. Il est a présent possible d'ouvrir notre page d'authentification et procéder a une première tentative.

La mise en oeuvre de Flask conjointement à nginx et uWSGI n'est finalement pas une grande partie de plaisir. Cependant, il n'est pas garantit que ce soit beaucoup plus simple avec une autre solution telle que apache + tomcat ou autre…

Comme d'habitude le code source est disponible sur github (https://github.com/spelle/GPIOsTickler), un bete “git clone” et le tour est joué.


Remarques, Propositions d'améliorations, Questions

Vous pouvez poster vos remarques, propositions d’amélioration, et questions sur le forum, dans la discussion prévue a cet effet : http://fablab-robert-houdin.org/fablab/phpBB-3.0.11-fr/phpBB3/viewtopic.php?f=3&t=34


tuto_a20-olinuxino-micro_un_serveur_web_en_python.txt · Dernière modification: 2014/02/10 21:08 par spelle