Warp 10 - Interactions avec une instance InfluxDB - FLoWS edition


08/10/2020 warp10 timeseries influxdb warpscript warpfleet flows

Maintenant que FLoWS est officiellement disponible, je vous propose de revisiter l’article de la semaine dernière Warp 10 - Interactions avec une instance InfluxDB en utliisant FLoWS à la place de WarpScript pour se faire une idée.

Pré-requis: warpfleet

Installons déjà warpfleet, le gestionnaire de package conçu pour Warp 10.

# Installation de npm
sudo dnf install -y npm

# installation de warpfleet
sudo npm install -g @senx/warpfleet

# Vérification de la bonne installation de warpfleet
wf version
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
1.0.31

Installation de l’extension warp10-ext-influxdb

Sans trop rentrer dans les détails de warpfleet, il utilise un système de namespace appelés “Groups” pour ces packages et qui permettent de définir ses propres dépots. Pour l’extension warp10-ext-influxdb, le “group” est io.warp10.

Ce qui pour l"installation donne la commande suivante :

# Si votre utilisateur n'a pas accès à /path/to/warp10, il vous faudra utiliser sudo
(sudo) wf g -w /path/to/warp10 io.warp10 warp10-ext-influxdb

warpfleet va vous demander quelle version de l’extension vous souhaitez puis va procéder à son téléchargement et son installation.

Cela donne :

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-influxdb
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-influxdb#1.0.1-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-influxdb-1.0.1-uberjar.jar successfully deployed
✔ Done

Note: Pour éviter un bug dans la fonction INFLUXDB.UPDATE identifié lors de la rédaction de ce billet, assurez-vous d’avoir une version >= 1.0.1

Ensuite, il faut créer le fichier /path/to/warp10/etc/conf.d/90--influxdb-extension.conf et y ajouter la ligne suivante:

warpscript.extension.influxdb = io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension

Je préfère créer un fichier plutôt que d’éditer un fichier existant pour le suivi des mises à jour et j’ai utilisé le prefix 90 car il n’est pas utilisé par les fichiers de Warp10.

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l’instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-09-17T10:59:23,742 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension'

Installation de FLoWS

Assurez-vous d’avoir préalablement une version >= 2.7.1 de Warp 10

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-flows
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-flows#0.1.0-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-flows-0.1.0-uberjar.jar successfully deployed
warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension
? Choose what you want to add (Press <space> to select, <a> to toggle all, <i> to invert selection)warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension
✔ warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension added to /opt/warp10/etc/conf.d/io.warp10-warp10-ext-flows.conf
✔ Done

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l’instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-10-08T10:59:51,288 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.ext.flows.FLoWSWarpScriptExtension'

Préalable sur FLoWS

FLoWS ne dispose pas encore d’un endpoint /flows ; nous continuons donc à utiliser les “endpoints historiques warpscript compatibles”.

Ce qui nous amène à réaliser une seule action en RPN/NPI (Notation Polonaise Inversée) lorsque l’on soumet du code FLoWS à Warp 10 :

# Multiligne WarpScript qui permet de mettre son code FLoWS
<'
... Code FLoWS ...
'>
# Applique la fonction FLOWS au code ci-dessus
FLOWS

Pour les types, les notations, etc - je voue renvoie au billet de blog introduisant FLoWS.

Requêtage d’une instance InfluxDB 1.x

La semaine dernière nous écrivions en WarpScript :

# Requête INFLUXQL et informations de connection à InfluxDB 1.X
{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
# On récupère une liste de liste de séries GTS. Il n'y a qu'un seul élément dans cette liste. Nous le prenons pour n'avoir plus qu'une liste de séries GTS.
0 GET
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

Cette semaine, nous écrivons en FLoWS :

<'
# Récupération de la liste de GTS
cpu_fetch = INFLUXDB.FETCH({'influxql':'select * from cpu where host=%27myHost%27 and time > now() - 1h', 'db':'myDatabase', 'password':'myPassword', 'user':'myUser', 'url':'http://url.to.influxdb:8086'})

# Récupération de la GTS en prenant l'index 0 de la liste
cpu = GET(cpu_fetch, 0)

# Affichage de la liste de GTS
return cpu
'>
FLOWS

On peut déjà noter :

  • la lecture du code correspond plus à nos habitudes dans les autres lanagage
  • les fonctions ont les mêmes noms, leur signature ne change pas non plus
  • le mot clé return est nouveau ici et permet d’afficher la GTS dans le studio par ex.
  • les tableaux (MAP) sont plus lisibles (arguments séparés par des virugules, la clé et la valeur sont séparés par deux points) et moins sources d’erreurs (gestion des espaces plus sensibles en WarpScript)

Le résultat est identique :

warp10 - influxdb 1

Pour illustrer cette liste de liste de GTS, si on veut récupérer la GTS du cpu idle, on voit dans le graphique que c’est la 5ème courbe, donc un indice 4.

En WarpScript :

{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
0 GET
'cpu' STORE
# Récupération de la 5ème liste (indice 4)
$cpu 4 GET

En FLoWS :

<'
cpu_fetch = INFLUXDB.FETCH({'influxql':'select * from cpu where host=%27myHost%27 and time > now() - 1h', 'db':'myDatabase', 'password':'myPassword', 'user':'myUser', 'url':'http://url.to.influxdb:8086'})
cpu = GET(cpu_fetch, 0)
# Récupération de la 5ème liste (indice 4)
# on pourrait écrire : return GET(cpu, 4) mais on peut aussi écrire de façon plus concise :
return cpu[4]
'>
FLOWS

warp10 - influxdb 2

Requêtage d’une instance InfluxDB 2.x

La semaine dernière, nous écrivions en WarpScript :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

En FLoWS :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
fluxquery = 'from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")'

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
params = {'flux':fluxquery, 'org':'myOrganisation', 'token':'myToken', 'url':'http://url.to.influxdb2:9999' }
# Sauvegarde de la liste dans une variable cpu
cpu = INFLUXDB.FLUX(params)
# Affichage de la liste de GTS
return cpu
'>
FLOWS

On obtient :

warp10 - influxdb 3

Si on veut comme précédemment avec InfluxQL afficher la courbe du CPU idle.

En WarpScript :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE

# Affiche la 7eme liste (incide 6)
$cpu 6 GET

En FLoWS :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
fluxquery = 'from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")'

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
params = {'flux':fluxquery, 'org':'myOrganisation', 'token':'myToken', 'url':'http://url.to.influxdb2:9999' }
# Sauvegarde de la liste dans une variable cpu
cpu = INFLUXDB.FLUX(params)
# Affiche la 7eme liste (incide 6)
return cpu[6]
'>
FLOWS

warp10 - influxdb 4

Sauvegarder des données dans InfluxDB

La semaine dernière, nous écrivions en WarpScript :

'<read_token>' 'readToken' STORE
'<write_token>' 'writeToken' STORE

# Récupération des dépenses sous la forme d'une série  (GTS)
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

# Récupération du chiffre d'affaires mensuel sous la forme d'une série  (GTS)
[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Calcul du résulat mensuel
$revenue $exp -
# Stockage de la série obtenue dans une série appelée "result"
"result" RENAME
{ "company" "cerenit" } RELABEL
$writeToken UPDATE

# Récupération du résultat mensuel sous la forme d'une série (GTS)
[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "result" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Utilisatoin de la fonction INFLUXDB.UPDATE qui prend la variable 'params' pour les paramètres de connection et une GTS ou liste de GTS pour les données à sauvegarder
$result $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "result" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
$result $params  INFLUXDB.UPDATE

En FLoWS, cela donne :

<'
# Gestion des tokens de lecture et écriture
readToken = '<read_token>'
writeToken = '<write_token>'

# Récupération de la liste de GTS dans Warp 10
expense_fetch = FETCH([readToken, 'expense', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
# On n'a toujours qu'une liste à 1 élement, on récupère donc la GTS en prenant l'index 0 de la liste
expense = expense_fetch[0]

# Même logique pour le chiffre d'affaires
revenue_fetch = FETCH([readToken, 'revenue', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
revenue = revenue_fetch[0]

# On calcule la résultat en faisant la différence entre les 2 GTS revenue et expense
r = revenue - expense
# la GTS obtenue n'ayant pas de nom, on lui en fournit un
RENAME(r, 'result')
# On ajoute les labels manquants
RELABEL(r, {"company":"cerenit"})
# On persiste la GTS dans Warp 10
UPDATE(r, writeToken)

# On récupère la contenu de la GTS result sur le même modèle que revenue et expense précédemment
result_fetch = FETCH([readToken, 'result', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
result = result_fetch[0]

# InfluxDB 1.X
# Définition des paramètres de connection
params = {'v1':true, 'url':"http://url.to.influxdb:8086", 'measurement':"result", 'db':"crntcompta", 'password':"myPassword", 'user':"myUser"}
# Persistance des données dans InfluxDB 1.x
INFLUXDB.UPDATE(result, params)

# InfluxDB 2.X
# Définition des paramètres de connection
params = {'v1':false, 'url':"http://url.to.influxdb:9999", 'measurement':"result", 'bucket':"crntcompta", 'token':"myToken", 'org':"myOrganisation" }
# Persistance des données dans InfluxDB 2.x
INFLUXDB.UPDATE(result, params)
'>
# Fin du code FLoWS
# Application de la fonction FLOWS à notre ensemble de code
FLOWS

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 5

Si au contraire, je veux regrouper plusieurs valeurs dans un même measurement InfluxDB, il faut passer une liste de GTS à INFLUXDB.UPDATE.

La semaine dernière, nous écrivions en WarpScript :

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "accountancy" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "accountancy" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

Cette semaine en FLoWS :

# InfluxDB 1.X
# Définition des paramètres de connection
params = {'v1':true, 'url':'http://url.to.influxdb:8086', 'measurement':'result', 'db':'crntcompta', 'password':'myPassword', 'user':'myUser'}
# Persistance des données dans InfluxDB 1.x en passant une liste de GTS
INFLUXDB.UPDATE([result, revenue, expense], params)

# InfluxDB 2.X
# Définition des paramètres de connection
params = {'v1':false, 'url':'http://url.to.influxdb:9999', 'measurement':'result', 'bucket':'crntcompta', 'token':'myToken', 'org':'myOrganisation' }
# Persistance des données dans InfluxDB 2.x en passant une liste de GTS
INFLUXDB.UPDATE([result, revenue, expense], params)
'>
FLOWS

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 6

Conclusion

Personnellement, au travers de cette exercice, je trouve FLoWS plutôt agréable à utiliser et il remplit bien son objectif de faciliter la prise en main de Warp 10 :

  • Même si je commençais à m’habituer à la NPI/RPN, avoir une approche plus traditionnelle évite de devoir se concentrer sur l’ordre,
  • La séparation par des virgules et/ou par des deux points (:) dans les listes et tableaux (MAP) facilitent leur lisiblité
  • La migration d’un code en Warpscript vers FLoWS peut se faire de façon très progressive puisque les deux peuvent cohabiter ; c’est d’ailleurs comme ça que j’ai procédé pour ce billet et valider ainsi que je ne perdais rien au fur et à mesure. Il suffit à la fin d’enlever tous les blocs intermédiaires pour n’avoir plus qu’un seul bloc de code en FLoWS. C’est plutôt malin ! Et si une extension ou un code ne serait pas encore disponible en FLoWS, il est possible de débrancher sur du Warpscript.
  • Il manque la complétion FLOWS dans le studio (et VSCode je présume) mais c’est pour bientôt !
  • Le code gagne en lisiblité car on identifie les blocs qui vont ensemble.

Pour illustrer ce dernier point : quand je repense au code du tutoriel sur les cyclone “Number per year”, nous avons ce code à un moment :

$fetch_wind $end $end $start - TIMECLIP NONEMPTY SIZE

La fonction TIMECLIP prend trois arguments sauf que là, j’en vois potentiellement cinq. Il faut alors comprendre que le troisième argument est en fait le résulat de l’opération $end $start -.

En FLoWS, on aura alors :

TIMECLIP(fetch_wind, end, end - start)

et au global (non testé) :

SIZE(NONEMPTY(TIMECLIP(fetch_wind, end, end - start)))

Ce qui me semble nettement plus clair/lisible.

Le pari de FLoWS était de rendre Warp 10 plus accessible et de rendre les développeurs productifs plus rapidement, il semblerait bien que les objectifs soient remplis.

Warp 10 - Interactions avec une instance InfluxDB


01/10/2020 warp10 timeseries influxdb warpscript warpfleet

Après les premiers pas avec Warp10 et en attendant que l’extension FLoWS soit disponible pour la version 2.7.0, j’ai mis à jour mon instance Warp 10 en 2.7.0 et j’ai voulu jouer avec l’extension warp10-ext-influxdb. Cette extension permet de requêter une instance InfluxDB 1.x ou 2.x avec du WarpScript.

Attention à ne pas confondre le plugin natif InfluxDB qui permet d’envoyer des métriques au format Line Protocol d’InfluxDB dans Warp10 et l’extension InfluxDB qui permet d’interagir avce une base InfluxDB en WarpScript.

Pré-requis: warpfleet

Installons déjà warpfleet, le gestionnaire de package conçu pour Warp 10.

# Installation de npm
sudo dnf install -y npm

# installation de warpfleet
sudo npm install -g @senx/warpfleet

# Vérification de la bonne installation de warpfleet
wf version
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
1.0.31

Installation de l’extension warp10-ext-influxdb

Sans trop rentrer dans les détails de warpfleet, il utilise un système de namespace appelés “Groups” pour ces packages et qui permettent de définir ses propres dépots. Pour l’extension warp10-ext-influxdb, le “group” est io.warp10.

Ce qui pour l"installation donne la commande suivante :

# Si votre utilisateur n'a pas accès à /path/to/warp10, il vous faudra utiliser sudo
(sudo) wf g -w /path/to/warp10 io.warp10 warp10-ext-influxdb

warpfleet va vous demander quelle version de l’extension vous souhaitez puis va procéder à son téléchargement et son installation.

Cela donne :

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-influxdb
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-influxdb#1.0.1-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-influxdb-1.0.1-uberjar.jar successfully deployed
✔ Done

Note: Pour éviter un bug dans la fonction INFLUXDB.UPDATE identifié lors de la rédaction de ce billet, assurez-vous d’avoir une version >= 1.0.1

Ensuite, il faut créer le fichier /path/to/warp10/etc/conf.d/90--influxdb-extension.conf et y ajouter la ligne suivante:

warpscript.extension.influxdb = io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension

Je préfère créer un fichier plutôt que d’éditer un fichier existant pour le suivi des mises à jour et j’ai utilisé le prefix 90 car il n’est pas utilisé par les fichiers de Warp10.

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l’instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-09-17T10:59:23,742 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension'

Requêtage d’une instance InfluxDB 1.x

Note: La librairie influxdb-java ne semble pas supporter un reverse proxy http/2 devant l’instance InfluxDB. Il faut donc accéder à InfluxDB en direct via le port 8086.

# Requête INFLUXQL et informations de connection à InfluxDB 1.X
{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
# On récupère une liste de liste de séries GTS. Il n'y a qu'un seul élément dans cette liste. Nous le prenons pour n'avoir plus qu'une liste de séries GTS.
0 GET
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

Dans ce qu’il faut noter ici:

  • INFLUXDB.FETCH prend ses paramètres dans une MAP ayant pour paramètres: influxql, db, password, user et url.
  • Pour la directive where, il faut encoder les apostrophes via %27 : en InfluxQL, on écrirait ... where host='myHost' ; là, il faut écrire where host=%27myHost%27
  • Si dans InfluxDB, on a un measurements (cpu) avec n items (usage_guest, usage_idle, etc), on a une conversion en une liste de n GTS avec une valeur chacune.
  • influxql prend une ou plusieurs requêtes séparées par des points virgules. Cela donnera en sortie plusieurs listes de listes de GTS (vu que comme dit au dessus, pour une requête influxql sur un measurement on a 1 à n GTS ; donc pour y requêtes, on aura y listes de n listes de GTS)

warp10 - influxdb 1

Pour illustrer cette liste de liste de GTS, si on veut récupérer la GTS du cpu idle, on voit dans le graphique que c’est la 5ème courbe, donc un indice 4.

{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
0 GET
'cpu' STORE
# Récupération de la 5ème liste (indice 4)
$cpu 4 GET

warp10 - influxdb 2

Requêtage d’une instance InfluxDB 2.x

Note: La librairie influxdb-client-java ne semble pas plus supporter un reverse proxy http/2 devant l’instance InfluxDB. Il faut donc accéder à InfluxDB en direct via le port 9999.

Si on fait une requête similaire en flux :

# Requête FLUX et informations de connection à InfluxDB 2.x
{ 'flux' "from(bucket: %22myBucket%22) |> range(start: -1h, stop: now()) |> filter(fn: (r) => r[%22_measurement%22] == %22cpu%22) |> filter(fn: (r) => r[%22cpu%22] == %22cpu-total%22) |> aggregateWindow(every: 1s, fn: mean, createEmpty: false) |> yield(name: %22mean%22)" 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# On récupère une liste de séries GTS
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

warp10 - influxdb 2

On note que la requête flux n’est pas très lisible de par l’encodage des guillemets et de son coté monoligne. On peut améliorer ça avec une variable STRING en multi-ligne :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

On y gagne en lisibilité et pas besoin d’encoder les guillements !

Dans les deux cas, on obtient :

warp10 - influxdb 3

On note d’ailleurs que les méta-données (nom du measurement, nom du champ et les tags sont repris sous la forme de labels)

Contraiement à la requête en InfluxQL, on ne peut passer qu’une requête à la fois mais ce qui permet d’avoir directment une liste de GTS puis la GTS. On n’a plus une liste de liste de GTS.

Si on veut comme précédemment avec InfluxQL afficher la courbe du CPU idle:

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE

# Affiche la 7eme liste (incide 6)
$cpu 6 GET

warp10 - influxdb 4

Je ne vais pas pousser l’exemple plus loin, il ne tient qu’à vous de poursuivre la manipulation de vos données en WarpScript. On pourrait se demander où mettre la limite entre la requête en Flux/InfluxQL et les manipulations à faire en WarpScript ensuite. Tout dépendra de votre cas d’usage.

Sauvegarder des données dans InfluxDB

Pour le moment, nous avons requêté des données stockées dans InfluxDB 1.x ou 2.x ; mais nous pouvons très bien imaginer un cas où les données sont issues d’une autre source de données ou bien ont été générées avec WarpScript mais qu’on veuille les persister dans InfluxDB 1.x ou 2.x

Reprenons mon exercice de compatbilité et de prédictions et sauvegardons tout ça dans InfluxDB.

Pour rappel, nous avons fait ceci :

'<read_token>' 'readToken' STORE
'<wrtie_token>' 'writeToken' STORE

# Récupération des dépenses sous la forme d'une série  (GTS)
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

# Récupération du chiffre d'affaires mensuel sous la forme d'une série  (GTS)
[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Calcul du résulat mensuel
$revenue $exp -
# Stockage de la série obtenue dans une série appelée "result"
"result" RENAME
{ "company" "cerenit" } RELABEL
$writeToken UPDATE

# Récupération du résultat mensuel sous la forme d'une série (GTS)
[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

Si je veux sauvegarder une série dans un measurement influxdb :

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "result" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Utilisatoin de la fonction INFLUXDB.UPDATE qui prend la variable 'params' pour les paramètres de connection et une GTS ou liste de GTS pour les données à sauvegarder
$result $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "result" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
$result $params  INFLUXDB.UPDATE

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 5

Si au contraire, je veux regrouper plusieurs valeurs dans un même measurement InfluxDB, il faut passer une liste de GTS à INFLUXDB.UPDATE.

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "accountancy" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "accountancy" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 6

On arrive au bout de ce billet, nous avons vu que nous pouvons :

  • En WarpScript, requêter des données stockées dans InfluxDB 1.x et 2.x
  • En WarpScript, manipuler des données issues de Warp 10 puis les stocker dans InfluxDB 1.x et 2.x

Nous pourrions aller plus loin avec :

  • des scénarios d’enrichissement de données en s’interfaçant par ex avec des données de sources SQL (référentiels, etc)
  • des scénarios d’analytics en croisant des données issues de Warp10, InfluxDB ou d’autres sources de données (bases SQL, etc)
  • des scénarios de projection en appliquant par exemple les algorythmes de machine learning sur des données issues d’InfluxDB

WarpScript semble ainsi permettre d’avoir un langage de manipulation de séries temporelles multi-sources et d’offrir une expérience unifiée de manipulatoin de ces données. Dans les prochains billets, nous explorerons d’avantage la partie visualisation et alerting.

Syndication

Restez informé(s) de notre actualité en vous abonnant au flux du blog (Atom)

Nuage de tags

kubernetes docker timeseries influxdb warp10 grafana traefik elasticsearch kafka postgres python ansible aws sécurité terraform mysql redis telegraf git ovh tick chronograf cloud dashboard docker-compose hashicorp timescaledb cassandra helm podman ptsm swarm test vector flux iot kapacitor rancher timescale cérénit influxdata log machine-learning monitoring postgresql raspberrypi s3 spark sql vscode arm bilan comptabilité confluent devops gitlab gitlab-ci iac java ksql microservice nomad perspective prometheus serverless service-mesh angularjs api bigdatahebdo cli cncf consul container discovery dns flows gcp gke graphql influxace ingress javascript npm opensource operator rook scaleway ssh stream vault warpscript windows architecture cert-manager containerd csp documentation elastic forecast geospatial golang hpkp json kafka-streams kibana kubedb lambda lean licence maesh mariadb microsoft mqtt nginx orientdb quasardb redhat registry rest rethinkdb reverse-proxy rgpd warpstudio wireguard agile anomalie apm arima azure bash big-data ceph certificat challenge cluster co2 continous-delivery continous-integration cookie datatask dataviz dbt deployment diff django edge esp32 facebook fec fluxlang gdpr google-analytics grav hsts http/3 https hypriot ia influxdays istio jq k3s lets-encrypt linux load-balancer longhorn meetup metabase mobile molecule mongodb nosql nvidia openebs openhab openssh ovhcloud pandas parquet percona performance php pip pipeline questdb reaper replication rootless rpi rsyslog runc résilience scale secrets société solr sre systemd tempo timezone tinygo tls virtualenv vitess vue.js wagtail warpfleet yarn accessibilité acme adoptopenjdk agpl akka alerte alertes alibaba amazon-emr amqp anonymisation anthos apache-pulsar ara arduino arrow artefact asgi automation automatisation automl awstats banque bastion beam beat bi bme680 bootstrap bounded-context branche brigade browser buildah buildkit calico cd cdc cdk centos certificats cgroups chart check checklist chrome ci cilium cio circuitpython clever-cloud clickhouse cloud-init cloud-native cloud-storage cloudflare clusterip cnab cni cockroachdb code codeurs-en-seine commit confluence conftest consul-connect context continous-deployment conventional-commit coreos cors covid19 cqrs crash cri cron crontab csi csrf css cto curl d3.js daemonset data data-engineer data-pipelining data.gouv.fr databricks datacenter date date-scientist ddd debezium debian delta deprek8 desktop devoxx dig distributed-systems dive docker-app docker-hub docker-registry dockerfile dockershim documentdb dog dokcer données-personnelles draft dredd drop-in dsi duckdb duration déploiement ebs ec2 elassandra electron elk engineering entreprise etcd euclidia event-sourcing faas falco falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink flovea fluentd font foundation framework frenchtech frontend fsync fugue fullstack git-filter-repo github gitignore gitpod glacier glowroot goaccess google google-cloud-next gpg gpu grep grid géospatial hacker hadoop haproxy harbor hdfs header holt-winters html html5 http httpx hue iaac ibm iiot immutable incident index indluxdata influxcloud infrastructure-as-code ingénierie inspec jenkins jless jquery jvm jwt k3d k6 k8s k9s kaniko katz kubeadm kubecon kubectl label laravel leap-second lens letsencrypt libssh linky linter liste-de-diffusion lmap loadbalancer logstash logstatsh loi loki lstm mailing-list management matomo maturité mesh mesos message metallb micro-service minio mot-de-passe multi-cloud médecine métrique n8n nebula network newsletter nodejs nodeport notebook notifications nrtsearch null numérique object-storage observability observabilité opa opendata openmetrics openshit openstack openweb opnsense over-engineering packaging partiql password persistent-volume-claim pico pipenv pivot pod portainer portworx prediction prescience privacy-shield production promql prophet prévision psp ptyhon publicité pubsub pulsar push pyenv pérénnité qualité quay queue quic ram rambleed raml react readme recaptcha recherche redistimeseries reindex reinvent reliability remote-execution repository responsive retention-policy revocation revue-de-code rexec rhel rkt robotframework rolespec root rpo rto rust rwd réseau résultat safe-harbor sarima scanner schema scp search select semiconducteur serverless-architecture service service-account service-worker setuptools sftp sha1 shard shard-duration shard-group sharding shell shipyard sidecar singer socket souveraineté-numérique spectre spinnaker sqlite sri ssh-agent ssl stabilité stash statistique stm32 storage sudo superset suse sympa sysdig syslog-ng sérénité task tavern template terracost terrascan test-unitaire thingspeak tidb tiers time timecale timer timestream training transformation travail trésorerie tsfel tsfr tsl ubuntu unikernel unit ux velero vendredi victoria-metrics vie-privée virtualbox virtualisation vm vnc volume voxxeddays vpc vpn wasm workflow yaml yield yq yubikey zip