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


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

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 l'item 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.
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 traefik grafana ansible kafka postgres warp10 elasticsearch python aws sécurité mysql redis terraform tick cassandra cloud helm ovh docker-compose git swarm telegraf timescaledb ptsm rancher résilience test chronograf flux gitlab log machine-learning prometheus architecture arm confluent dashboard devops hashicorp iac java ksql microservice monitoring s3 serverless spark vscode angularjs api bilan cert-manager cncf container cérénit dns gcp graphql ingress javascript kapacitor opensource operator optimisation perspective podman raspberrypi service-mesh sql ssh stream warpscript windows csp documentation elastic flows forecast gke hpkp influxace jenkins kafka-streams kibana kubedb lambda lean licence maesh maintenance mariadb microsoft mobile nginx npm orientdb performance pipeline redhat registry rest rethinkdb reverse-proxy rook sauvegarde scaleway vector agile apm automatisation azure bash big-data bigdatahebdo ceph certificat ci/cd cli cluster comptabilité containerd continous-delivery continous-integration cookie dataviz deployment diff fluxlang framework gdpr gitlab-ci grav hsts http/3 https hypriot hébergement influxdata influxdays iot istio jq json k3s lets-encrypt linux load-balancer longhorn meetup molecule mongodb nomad nosql nvidia openebs openssh ovhcloud percona php pip postgresql reaper replication rootless rpi rsyslog runc scale secrets société solr sre systemd timescale timezone tls vault virtualenv vitess vue.js wagtail warpfleet yarn accessibilité acme akka alerte alibaba amazon-emr amqp anomalie anonymisation anthos apache-pulsar ara arima arrow artefact audit bastion beam beat bounded-context branche brigade browser buildkit cahier-des-charges calico cassandra-reaper cd cdc cdk centos centralisation-de-logs certificats cgroups chart checklist chrome ci cilium cloud-init cloud-native cloud-storage clusterip cnab cni cockroachdb code codeurs-en-seine commit confluence conftest consul context continous-deployment conventional-commit coreos cors covid19 cqrs crash cri cron crontab csi csrf css curl d3.js daemonset data data-engineer data-pipelining data.gouv.fr datacenter date date-scientist ddd debezium debian delta deprek8 desktop devoxx dig distributed-systems dive docker-app docker-hub docker-registry docker-swarm dockershim documentdb dog dokcer données-personnelles draft drop-in duration déploiement développement-du-site e-commerce ebs ec2 edge elassandra electron elk engineering entreprise ergonomie etcd event-sourcing faas facebook faisabilité falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink fluentd formation foundation frontend fsync fullstack github gitignore glacier glowroot golang google google-cloud-next gpg gpu grid géospatial hacker hadoop haproxy harbor hdfs header html html5 http hue ia iaac ibm immutable incident index influxcloud infrastructure-as-code ingénierie inspec jquery jwt k3d k8s k9s kotlin kubeadm kubecon kubectl label laravel letsencrypt libssh linky linter liste-de-diffusion lmap loadbalancer logstash logstatsh loi mailing-list management maturité mesh mesos message metallb micro-service mot-de-passe mqtt multi-cloud médecine métrique network newsletter nodeport null object-storage observability observabilité opa opendata openhab openmetrics openshit openstack openweb over-engineering packaging pandas parquet partiql password persistent-volume-claim pipenv pod portainer portworx prediction prescience production promql prévision ptyhon publicité pubsub pulsar push pyenv pérénnité qualité quasardb quay questdb queue quic ram rambleed raml react recaptcha recherche redistimeseries reindex reinvent reliability remote-execution repository responsive revocation revue-de-code rexec rgpd rhel rkt rolespec root rpo rto rust rwd safe-harbor scalabilité scanner schema scp sdk search select serverless-architecture service service-account service-worker setuptools sftp sha1 sharding shell shipyard sidecar souveraineté-numérique spinnaker spécifications sri ssh-agent ssl stabilité stash statistique storage sudo superset suse sympa syslog-ng sérénité template terracost terrascan test-unitaire tidb tiers timer timestream training transformation travail tsl ubuntu unikernel unit ux vendredi victoria-metrics vie-privée virtualbox virtualisation vm vnc volume voxxeddays vpc warpstudio web yaml yq yubikey