Sommaire
- Ingrédients
- La pièce de résistance
- La recette
- Diverses Questions Réponses
- Un forth dans la boucle était il nécessaire ?
- Un vrai command & control préférerait un protocole plus passe partout comme https pour être moins visible
- Et si je voulais sécuriser ?
- Où est l'arborescente complète du projet il manque les plugins de mesure ?
- Mais c'est bête en fait…
- Des idées de futurs ?
- Les sorties actuellement ressemblent au format d'entrée … C'est suspect non ?
Ingrédients
Cette recette nécessite : python, et en dépendances : paho-mqtt, confined, ainsi qu'un serveur MQTT (mosquitto avec ses utilitaires en ligne de commande) correctement configurés.
La pièce de résistance
Pour tout process que l'on veut piloter on va écrire du code python comme :
importpaho.mqtt.clientasmqttfromtimeimporttime,sleepfromconfinedimportparse,Value,popfromsubprocessimportPopen,PIPEimportpathlibimportosimportsocketstack=[]client_id=socket.gethostname()show_must_go=Falsedefon_connect(client,userdata,flags,reason_code,properties):print(f"Connected with result code {reason_code}")client.subscribe(f"BUS/{client_id}")client.subscribe(f"BUS")defon_lun(*a,**kw):kw["ctx"]["state"]="RAZ"defon_set_time_slice(*a,**kw):kw["ctx"]["time_slice"]=stack.pop().floatdefon_ping(*a,**kw):globalclient_idclient=kw["ctx"]["client"]client.publish("RES",f"'{client_id}':PONG")defon_sel(stack,**kw):globalclient_id,show_must_goifstack.pop().str==client_id:show_must_go=Truedefon_unsel(stack,**kw):globalclient_id,show_must_goifstack.pop().str==client_id:show_must_go=Falsedefon_test(stack,**kw):print("Yo")ctx=dict(cap=["www","forth"],time_slice=10,dispatch=dict(lun=on_lun,ping=on_ping,sel=on_sel,unsel=on_unsel,tsset=on_set_time_slice,_TEST=on_test,),)defon_message(client,userdata,msg):globalstack,ctxctx["client"]=clientclient.publish("RES",str(parse(ctx,msg.payload.decode(),data=stack,)))mqttc=mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,client_id=client_id)mqttc.on_connect=on_connectmqttc.on_message=on_messagemqttc.username="pub"mqttc.password="pub"mqttc.tls_set(keyfile="./cfg/pub.key",certfile="./cfg/pub.crt",ca_certs="./cfg/RootCA.crt",tls_version=2)mqttc.connect("badass.home",8883,60)mqttc.loop_start()os.chdir(os.path.dirname(__file__))plugins=pathlib.Path("../plugin")whileTrue:start=time()ifshow_must_go:forpinplugins.glob("*_enabled"):withPopen([p,],stdout=PIPE,stdin=PIPE,stderr=PIPE,bufsize=0,)aswriter:whileres:=writer.stdout.read():writer.stdout.flush()formsginres.split():mqttc.publish(f"DATA/{client_id}",msg.decode())mqttc.publish("DATA/",f"{client_id}:core.processing_time:{time()-start}:GAUGE")iftime()-start<ctx["time_slice"]:sleep(ctx["time_slice"]-(time()-start))Le client est en mode parano : TLS activé avec login/pass enforcé coté serveur par MQTT.
On va définir 2 commandes :
pub.sh
#!/usr/bin/env bashHERE=$( dirname $0)pushd$HERE
mosquitto_pub --cert ../cfg/4711.crt --key ../cfg/4711.key --cafile ../cfg/RootCA.crt -h badass.home -t BUS -u 4711 -P 4711 -m "$1"popdqui publie en MQTT sur un canal BUS
et
listen.sh
#!/usr/bin/env bashHERE=$( dirname $0)pushd$HERE
mosquitto_sub -h badass.home -t RES/# &
mosquitto_sub -h badass.home -t DATA/# &popdLa recette
Pour pinger votre agent il vous suffit de faire
pub.sh PING
ce qui répond
'badass':PONG
Pour tester la fonction de test locale :
pub.sh _TEST
Normalement sur sortie standard du process python vous pouvez lire : 'Yo'.
pub.sh "'badass': SEL"
La sortie de LISTEN devrait montrer des lignes comme suit:
badass:cpu.load:1.39:GAUGEbadass:stat.procs_running:1:GAUGEbadass:stat.procs_blocked:1:GAUGEbadass:stat.intr:235643164:DERIVEbadass:stat.ctxt:474453646:DERIVEbadass:stat.processes:237036:DERIVEbadass:ps.processes:320:GAUGEbadass:ps.uninterruptible:1:GAUGEbadass:ps.runnable:1:GAUGEbadass:ps.sleeping:256:GAUGEbadass:ps.idle:62:GAUGEbadass:ps.stopped:0:GAUGEbadass:ps.paging:0:GAUGEbadass:ps.dead:0:GAUGEbadass:ps.zombie:0:GAUGEbadass:files.opened:17184:GAUGEbadass:core.processing_time:0.039983272552490234:GAUGEQui sont le résultat de l'enclenchement de la mesure qui se fait toutes les 10 secondes par défaut
pub.sh "20: TSSET"
Passe les mesures à 20 secondes d'écart.
pub.sh "'badass': UNSEL"
Arrête les mesures.
Diverses Questions Réponses
Un forth dans la boucle était il nécessaire ?
Non.
Mais il se trouve que j'en ai un en stock et que je rêvais de l'utiliser pour faire des tâches innocentes (principalement du templating).
Il est pas utilisé, mais disons que si vous faisiez :
pub.sh "2: 2: ADD"
en sortie vous auriez autant de 4: qu'il y a d'agents connectés sur le BUS.
Vous avez de base une calculette 4 opérations distribuées en prime.
Un vrai command & control préférerait un protocole plus passe partout comme https pour être moins visible
Ça tombe bien paho-mqtt et mosquito permettent de passer en websocket :)
Et si je voulais sécuriser ?
MQTT le permet avec des ACL par sujets.
Où est l'arborescente complète du projet il manque les plugins de mesure ?
Mais c'est bête en fait…
C'est pas faux, c'est pour ça que je sais pas quoi faire avec ce « projet »
Trop petit pour être un projet, ultra dur à tester, mais assez rigolo pour être utile
Des idées de futurs ?
Un projet « Bus Of Things » (BOT) qui standardiserait les commandes envoyées et leurs API pour faire comme une sorte d'ansible.
Une logique d'IPC/messaging générique pour des systèmes distribués (inclurais la gestion de process & co).
Un FORTH qui verrait toute fonction/agent comme reliée à un BUS MQTT et pour lequel les messages serait du FORTH qui agirait sur la fonction que s'appelerio objective FORTH. (Ça implique de sacrément développé la partie langage).
Pleins d'idées, trop d'idées …
Les sorties actuellement ressemblent au format d'entrée … C'est suspect non ?
Oui, j'ai envie de tester de laisser l'orchestrateur accepter des injections de code depuis les agents. Ex légitime, quand une sonde de mesure est en OVERRUN (trop de temps passé à mesurer comparé à une cadence attendue) qu'elle puisse changer la « clock » avec TSSET de l'orchestrateur.
J'ai envie d'expérimenter des systèmes scheduler less où chaque agent peut devenir le contrôleur et prendre la main et/ou modifier l'orchestrateur qui envoie les commandes.
Commentaires :voir le flux Atomouvrir dans le navigateur