Ma comptabilité, une série temporelle comme les autres - partie 3 - exécution distante


19/02/2021 warp10 timeseries comptabilité remote execution rexec lmap template

Suite de notre épopée :

A l’issue du précédent billet, depuis le WarpStudio et en stockant les données dans la sandbox, nous avons manipuler les données pour :

  • refaire les précisions de juin à décembre 2020 à partir des données de 01/2017 à 05/2017
  • comparer ses prévisions avec les résultats réels
  • faire les prévisions pour 2021.

Cependant, cela n’est pas parfait :

  • La durée de vie des données dans la sandbox est limitée à 2 semaines
  • Il faut copier/coller ses requêtes dans le studio pour obtenir les graphiques
  • Mais cela permet d’évaluer les extensions commerciales déployées sur la sandbox mais que vous n’avez pas forcément sur votre instance Warp 10 (au moins pour le moment)

Nous allons voir aujourd’hui comment rappatrier les données générées dans sa propre instance Warp 10.

Génération des prévisions

Pour commencer du bon pied et être sur que tout le monde est au même niveau, nous allons regénérer les prévisions et chaque prévision sera alors stockée dans une sériée dédiée. Cela nous permet d’avoir notre jeu de données de départ. On doit pouvoir sauver directement nos données dans une base distante, mais pour simplifier le tutoriel, nous allons le faire en deux étapes.

'<read token>' 'readToken' STORE
'<write token>' 'writeToken' STORE

// Récupération des séries 2017 > 2020
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

// Prévision sur les 12 prochains mois avec AUTO
// On n'a pas besoin d'afficher les données
// Donc plus besoin de les stocker sous forme de variable avec utilisation de la fonction STORE
// Par contre, on veut perstister les données en base
// ce qui se fait avec UPDATE et l'utilsation d'un token en écriture
[ $result mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_result" RENAME
$writeToken UPDATE

[ $revenue mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_revenue" RENAME
$writeToken UPDATE

[ $exp mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_expense" RENAME
$writeToken UPDATE

# Prévisions avec SAUTO
[ $result mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_result" RENAME
$writeToken UPDATE

[ $revenue mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_revenue" RENAME
$writeToken UPDATE

[ $exp mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_expense" RENAME
$writeToken UPDATE

Si nous vouulons vérifier que vos données sont bien en base, il faut utiliser FETCH :

// Ex de FETCH avec la série auto_revenue
'<read token>' 'readToken' STORE
[ $readToken 'auto_revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2022-01-01T00:00:00Z' ] FETCH
0 GET

Attaquons maintenant la grande traversée des données vers mon instance Warp 10.

Activation de l’exécution à distance

Alors on pourrait simplement migrer les données à coup de “curl in/curl out” mais l’idée ici est plus d’illustrer les interactions possibles entre des instances Warp 10.

Pour exécuter du warpscrip sur une instance distante, il faut utiliser la fonction REXEC

Cette exécution distante est désactivée par défaut, il faut donc activer cette extension:

Dans /path/to/warp10/etc/conf.d/70--extensions.conf, nous avons :

// REXEC
#warpscript.extension.rexec = io.warp10.script.ext.rexec.RexecWarpScriptExtension
#warpscript.rexec.endpoint.patterns = .*
// REXEC connect timeout in ms (default = 0, no timeout)
#warpscript.rexec.timeout.connect = 3600000
// REXEC read timeout in ms (default = 0, no timeout)
#warpscript.rexec.timeout.read = 3600000

Décommentons ces lignes et relançons warp 10.

Pour valider que REXEC est bien activé, depuis le studio, nous pouvons faire un test simple:

'2 2 +' 'https://warp10.url:port/api/v0/exec' REXEC

La réponse est 4.

Attention, il faut choisir votre instance comme endpoint dans la liste déroulante du studio. Si vous êtes sur le endpoint de la sandbox, vous auez le message d’erreur suivant :

https://sandbox.senx.io/api/v0/exec
line #2: Exception at ''2%202%20+' 'https://warp10.url:port/api/v0/exec' =>REXEC<=' in section [TOP] (REXEC encountered a forbidden URL 'http://warp10.url:port/api/v0/exec')

En effet, depuis la Sandbox, il n’est pas possible d’accéder à n’importe quelle machine par mesure de sécurité.

Requêtage simple à distance

Dans le studio, à partir de maintenant, il doit être configuré pour utliiser votre instance Warp 10 comme endpoint.

Le test simple étant fonctionnel, passons à un test un peu plus compliqué, à savoir recupérer nos données hébergées depuis la sandbox en lecture pour le moment.

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// On introduit ici la notion de template - comme on va vouloir récupérer plusieurs séries avec les mêmes paramètres
// Autant automatiser un peu et s'appuyer sur une boucle ! :-)
// On crée donc un TEMPLATE pour la fonction FETCH qui va récupérer un token en écriture
// et un nom de classe permettant de récupérer nos GTS.
// Rappel le <' ... '> permet de faire des strings en multi-lignes
// On stocke le template sous la forme d'une variable fetchTpl.
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Avec la fonction TEMPLATE, on remplace les clés par leurs valeurs en fournissant le template
// et un dictionnaire à la fonction.
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken 'remoteClass' 'revenue' } TEMPLATE

// Execution de la requête distante avec REXECZ
// La différence avec REXEC est qu'une compression est appliquée sur la réponse à la requête
$url REXECZ
// La liste de GTS issue de FETCH ne contient qu'une liste, on prend donc la première
0 GET
// Stockage sous la forme d'une variable
'revenueGTS' STORE
// Affichage de la série
$revenueGTS

Si vous allez dans l’onglet dataviz, vous pouvez constater que vos données issues de la sandbox mais qui ont transité via votre instance sont bien disponibles.

Transfert des données - 1 série

Si nous commençons par une seule série :

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Template de code warpscript
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Substitution des variables
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken 'remoteClass' 'revenue' } TEMPLATE
// Exécution de la requête
$url REXECZ
// La liste de GTS issue de FETCH ne contient qu'une liste, on prend donc la première
0 GET
// Il faut renommer "localement" la série avant de pouvoir la stocker dans l'instance
// Peut éviter de mauvaises manipulations que l'on pourrait regretter :-)
"revenue" RENAME
// Persistance des données
$instanceWriteToken UPDATE

Il y a quelques occurences de “revenue” en dur dans le code, il va falloir améliorer cela.

Transfert des données - plusieurs séries

Et maintenant, traitons nos 9 series d’un coup

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Template de code warpscript
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Création d'une liste avec nos 9 séries
// C'est cette liste que nous allons passer ensuite dans une MACRO.
// Cette MACRO va être exécutée sur chaque élément de la liste via l'utilisation de la fonction LMAP
// https://www.warp10.io/doc/LMAP
[ 'revenue' 'exp' 'result' 'auto_revenue' 'auto_result' 'auto_expense' 'sauto_revenue' 'sauto_result' 'sauto_expense' ]
// Début de la MACRO
<%
  // On récupère la valeur de la liste que l'on stocke sous la forme d'une variable
  'remoteClass' STORE

  // Substitution des valeurs de template
  $fetchTpl
  { 'remoteReadToken' $sandboxReadToken 'remoteClass' $remoteClass } TEMPLATE

  // Exécution distante de la requête
  $url REXECZ
  // On récupère ici une liste de GTS - plutôt que d'en extraire la GTS comme précédemment
  // on va garder une liste de GTS à 1 élément, mais ce qui permet à nouveau d'utiliser la fonction LMAP
  // Sur chaque entrée de la liste, une seconde macro est appliquée
  // Le contenu de notre macro consiste à utliser la fonction RENAME
  // '+' RENAME, cela revient à renommer la GTS en prenant le même nom que celui qui est fourni
  // '+x' RENAME aurait ajouté un x au nom de la série
  // Il reste l'index de la liste à traiter - soit on le supprime avec DROP
  //<% DROP '+' RENAME %> LMAP
  // Soit on passe F comme 3ème argument à LMAP - cela permet d'ignorer cet index
  // <% '+' RENAME %> F LMAP
  // Prenons la seconde forme :
  <% '+' RENAME %> F LMAP
  // Toutes nos séries ont été correctement renommées !
  // On persiste la GTS dans la base locale
  $instanceWriteToken UPDATE
%>
// Fin de la MACRO
// Application de la fonction LMAP pour que notre macro soit exécutée sur chaque élément de la liste.
// Comme on ne veut que les valeurs de la liste et pas les index, on positionne aussi F
// comme 3ème argument à LMAP
F LMAP

Et voilà, nos données ont été récupérées de la Sandbox et stockées dans notre instance locale.

Bonus

Une version alternative - dans mes données, je peux tricher et ne filtrer que sur le label company avec pour valeur cerenit:

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Warpscript template
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '~.*'
    'labels' { 'company' 'cerenit' }
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Substitution dans le template
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken } TEMPLATE
// Execution de la requête
$url REXECZ
// Renommage des séries
<% '+' RENAME %> F LMAP
// Presistence des données
$instanceWriteToken UPDATE

Bravo si vous m’avez suivi jusqu’ici, nous avons pu voir l’utilisation de :

  • Comment permettre un requêtage à distance d’une instance Warp 10 avec les fonctions REXEC et REXECZ
  • Comment traiter dynamiquement notre liste de GTS avec LMAP et une MACRO
  • Comment faire un template warpscript et de la substitution de variables avec TEMPLATE

Nous verrons dans un prochain épisode :

  • Comment utiliser Discovery pour faire nos premiers dashboards
  • Pourquoi/comment utiliser des macros coté server (spoiler: pour éviter que vos tokens se retrouvent dans votre navigateur à l’exécution des dashboards)

Syndication

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

Nuage de tags

docker kubernetes influxdb timeseries warp10 grafana traefik python ansible elasticsearch kafka postgres aws sécurité terraform mysql redis telegraf dashboard docker-compose git ovh tick cassandra chronograf cloud hashicorp helm timescaledb flux podman ptsm swarm test vector kapacitor rancher résilience timescale gcp gitlab influxdata iot log machine-learning monitoring postgresql prometheus raspberrypi s3 spark sql vscode api architecture arm bilan comptabilité confluent cérénit devops gitlab-ci iac java ksql microservice nomad optimisation perspective serverless service-mesh angularjs cert-manager cli cncf consul container discovery dns flows gke graphql influxace ingress javascript npm opensource operator performance pipeline rook scaleway ssh stream vault warpscript windows bigdatahebdo containerd csp documentation elastic forecast geospatial golang hpkp jenkins json kafka-streams kibana kubedb lambda lean licence maesh maintenance mariadb microsoft mobile mqtt nginx orientdb quasardb redhat registry rest rethinkdb reverse-proxy rgpd sauvegarde warpstudio wireguard agile anomalie apm arima audit automatisation azure bash big-data ceph certificat challenge ci/cd cluster co2 continous-delivery continous-integration cookie data datatask dataviz dbt deployment diff django edge esp32 facebook fec fluxlang framework gdpr google-analytics grav hsts http/3 https hypriot hébergement ia influxdays istio jq k3s lets-encrypt linux load-balancer longhorn meetup metabase molecule mongodb nosql nvidia openebs openhab openssh ovhcloud pandas parquet percona php pip questdb reaper replication rootless rpi rsyslog runc 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 alerting alibaba amazon-emr amqp anonymisation anthos apache-pulsar ara arduino arrow artefact asgi automation automl awstats banque bastion beam beat bi bme680 bootstrap bounded-context branche brigade browser buildah buildkit cahier-des-charges calico cassandra-reaper cd cdc cdk centos centralisation-de-logs certificats cgroups chart check checklist chrome ci cilium 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 curl d3.js daemonset 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 docker-swarm dockerfile dockershim documentdb dog dokcer données-personnelles draft dredd drop-in duckdb duration déploiement développement-du-site e-commerce ebs ec2 elassandra electron elk engineering entreprise ergonomie etcd euclidia event-sourcing faas faisabilité falco falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink fluentd font formation foundation frenchtech frontend fsync fugue fullstack git-filter-repo github gitignore gitpod glacier glowroot go 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 immutable incident index indluxdata influxcloud infrastructure-as-code ingénierie inspec jless jquery jvm jwt k3d k6 k8s k9s kaniko katz kotlin 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 scalabilité scanner schema scp sdk 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 spécifications 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 web workflow yaml yield yq yubikey zip