Locust est un framework de test de montée en charge open source (sous licence MIT), facile à utiliser, distribué, écrit en Python. Il permet de définir le comportement des utilisateurs avec du code Python, simuler plusieurs millions d'utilisateurs simultanés, définir et maîtriser la montée en charge et peut être configuré en multi-machines.

Locust est facile à installer et sa documentation est bien fournie.

Dans ce mini tutoriel, nous allons utiliser Locust pour tester la montée en charge d'Odoo et pour cela, nous allons nous baser sur OdooLocust, un package python basé sur odoolib et locust.

Installation

Dans un environnement virtuel python, nous allons installer locustio, locust et odoo-client-lib.

virtualenv sandbox --python=python3.5
source sandbox/bin/activate

pip install odoo-client-lib==1.2.2 locustio locust

Nous allons également installer OdooLocust

git clone https://github.com/anybox/OdooLocust.git
cd OdooLocust/
sandbox/bin/python setup.py install

Scénario

Sur une version 12.0 d'Odoo communautaire, nous allons effectuer le scénario suivant:

  1. Lire les ID des partenaires
  2. Lire les ID des Produits
  3. Créer un bon de commande

Ci-dessous, le contenu du fichier odoo_locust.py utilisé

from locust import task, TaskSet


class SellerTaskSet(TaskSet):

    @task(10)
    def read_partners(self):
        cust_model = self.client.get_model('res.partner')
        cust_ids = cust_model.search([])
        cust_model.read(cust_ids)

    @task(5)
    def read_products(self):
        prod_model = self.client.get_model('product.product')
        ids = prod_model.search([])
        prod_model.read(ids)

    @task(20)
    def create_so(self):
        prod_model = self.client.get_model('product.product')
        cust_model = self.client.get_model('res.partner')
        so_model = self.client.get_model('sale.order')

        cust_id = cust_model.search([('name', 'ilike', 'fletch')])[0]
        prod_ids = prod_model.search([('name', 'ilike', 'drawer')])

        so_model.create({
            'partner_id': cust_id,
            'order_line': [(0, 0, {'product_id': prod_ids[0],
                            'product_uom_qty':1}),
                           (0, 0, {'product_id': prod_ids[1],
                            'product_uom_qty':2})],

        })

@task utilise un argument de pondération facultatif qui peut être utilisé pour spécifier le taux d’exécution de la tâche. Dans l'exemple ci-dessus, la 3e tâche (create_so) sera exécutée deux fois plus que la 1ere tâche (read_partners) et 4 fois plus que la 2e tâche (read_products)

Maintenant, nous allons définir les paramètres qui seront utilisés par Locust dans un fichier nommé profile.py

from OdooLocust import OdooLocust
from odoo_locust import SellerTaskSet


class Seller(OdooLocust.OdooLocust):
        # Odoo options
        host = "127.0.0.1"
        database = "locust"
        login = "admin"
        password = "admin"

        # Locust options
        min_wait = 100
        max_wait = 1000
        weight = 3

        task_set = SellerTaskSet

Explications

  1. host, database, login, password: addresse IP du serveur, nom de la base de données, login et mot de passe de l'utilisateur Odoo.
  2. max_wait = 1000: temps de pondération maximum entre l'exécution des tâches
  3. min_wait = 100: temps de pondération minimum entre l'exécution des tâches
  4. La classe SellerTaskSet définit le comportement d'exécution
  5. weight = 3: Cette attribut permet de définir la probabilité de sélection. Ceci est nécessaire lorsqu'on a plusieurs classes Locust (ce qui n'est pas notre cas). Plus cette valeur est élevée, plus la chance d'être choisi est grande.

Supposons, par exemple, que les utilisateurs web soient trois fois plus fréquents que les utilisateurs mobiles:

class WebUserLocust(Locust):
    weight = 3
    ...

class MobileUserLocust(Locust):
    weight = 1
    ...

Exécution

Exécutez le fichier python profile.py avec la commande suivante

locust -f profile.py Seller 

Maintenant, allez à l'addresse http://127.0.0.1:8089 sur votre navigateur web.

Dans l'interface ci-dessus, Locust a besoin de deux données pour commencer à stresser odoo.

  1. Nombre d'utilisateurs à simuler
  2. Taux d'éclosion: à quelle vitesse veulent-ils engendrer les utilisateurs

Example:

Number of users to simulate : 10  | Hatch rate (user spawned / second) : 2

Lorsque nous commençons le test de charge avec cette configuration, Locust génère deux nouveaux utilisateurs toutes les secondes jusqu'à ce qu'il atteigne le nombre total d'utilisateurs à simuler (ce qui est 10 dans ce cas). Après 05 secondes, vous aurez un essaim de 10 utilisateurs simulés.

Pour contrôler le nombre de demandes par seconde, vous devrez définir un max_wait en conséquence.

Le résultat du test pour l'exemple ci-dessus est le suivant.

Allez plus loin...

Les tableaux générés par Locust sont agréables, mais nous préférerions voir les résultats sur un graphique. Ce problème est connu et plusieurs personnes souhaitent ajouter une interface graphique à Locust. Plusieurs propositions permettant d'afficher des graphiques de données sur Locust ont déjà été faites. Dans cet exemple, nous allons utiliser la bibliothèque de visualisation interactive Python Bokeh.

Son installation est simple:

pip install bokeh

Voici un exemple d'exécution du serveur Bokeh.

Nous pouvons obtenir des données Locust au format JSON en visitant http://localhost:8089/stats/request. Ces données devraient ressembler à ceci:

{
  "current_response_time_percentile_50": 260, 
  "current_response_time_percentile_95": 4200, 
  "errors": [], 
  "fail_ratio": 0.0, 
  "state": "running", 
  "stats": [
    {
      "avg_content_length": 28.0, 
      "avg_response_time": 139.6, 
      "current_rps": 0.0, 
      "max_response_time": 269, 
      "median_response_time": 120, 
      "method": "Odoo JsonRPC", 
      "min_response_time": 86, 
      "name": "common : login", 
      "num_failures": 0, 
      "num_requests": 10
    }, 
    {
      "avg_content_length": 181.4791389848525, 
      "avg_response_time": 1161.0988572947117, 
      "current_rps": 7.4, 
      "max_response_time": 7959, 
      "median_response_time": 220, 
      "method": null, 
      "min_response_time": 17, 
      "name": "Total", 
      "num_failures": 0, 
      "num_requests": 7526
    }
  ], 
  "total_rps": 7.4, 
  "user_count": 10
}

Pour afficher ces données sur des graphiques interactifs, nous allons créer un script python basé sur l'utilisation de la bibliothèque de visualisation python Bokeh, et le placer dans le répertoire dans lequel se trouve notre fichier profile.py

Un exemple de script est disponible ici. Ci-dessous, le résultat