[PYTHON] J'ai essayé de décrire le trafic en temps réel avec WebSocket

Ceci est l'article sur le 11ème jour de NetOpsCoding AdventCalender.

Cette fois, je vais implémenter un graphique de trafic qui est presque toujours confirmé lors des opérations réseau, en utilisant une technologie appelée * WebSocket *. La démo et le code source sont collés à la fin.

** Cette méthode d'implémentation est implémentée en se référant à l'outil utilisé par @yuyarin pour le monitoring dans Cedec2015. ** **

1.Tout d'abord

Cacti, Zabbix, MRTG, etc. sont réputés comme outils de surveillance du trafic. Dans la plupart des cas, l'intervalle d'acquisition SNMP est d'environ 5 minutes. Fondamentalement, je suis satisfait de la confirmation, mais dans un travail réel tel que l'ouverture d'une nouvelle ligne / l'ajustement du trafic, je veux souvent voir le trafic à * à ce moment *. En fonction de l'appareil, le trafic en temps réel peut être surveillé sur une base CLI, mais est-il difficile de remarquer des changements dans l'état de plusieurs emplacements avec CLI, et y a-t-il un retard dans la détection d'anomalies?

Afin de résoudre un tel problème, je vais créer un graphique qui est * visuellement facile à comprendre et peut capturer les changements de trafic à court terme en temps réel.

  1. Comment dessiner en temps réel sur le Web = Il existe deux méthodes principales pour dessiner dynamiquement un graphique sur un écran Web.

WebSocket est une technologie relativement nouvelle. Vous pouvez décider librement du moment de l'envoi et de la réception des données et de la méthode de communication. Une fois que vous avez publié une session, vous pouvez facilement transférer des données dans les deux sens.

Le dessin graphique peut être réalisé avec l'un ou l'autre, mais de toute façon, je vais essayer d'utiliser WebSocket, qui semble être applicable à d'autres implémentations d'outils! !! Quoi qu'il en soit, utilisons-le immédiatement!

  1. Environnement d'exécution =

Pour afficher sur le Web, une plate-forme telle que nginx ou apache est requise. Ce serveur de démonstration utilise nginx. De plus, 8080 est utilisé pour la communication WS cette fois.

La bibliothèque WebSocket utilise tornado. Il y a Gevent-WebSocket, ws4py, etc., mais la tornade m'est venue le plus.

En outre, bien que HighCharts soit utilisé comme outil de dessin de graphique, il n'y a aucun problème avec d'autres outils de graphique qui peuvent être ajoutés dynamiquement. De plus, HightCharts est facturé sauf pour un usage personnel, donc ceux qui sont concernés devraient utiliser d'autres outils. ccchart est recommandé car il contient de nombreux échantillons.

  1. Implémentation côté serveur WebSocket =

Implémentons le côté serveur. L'implémentation côté serveur de WebSocket n'est pas difficile si seul le point de "traitement asynchrone" est supprimé. Lorsqu'il écoute un URI spécifique et que le client est connecté, il devient possible d'échanger librement des données entre eux, il devient donc possible d'envoyer le résultat de l'acquisition de SNMP. SNMP est acquis en exécutant des commandes par sous-processus, et les résultats sont extraits avec des expressions régulières. De plus, cette fois, l'OID qui regarde le compteur du NIC du propre serveur est spécifié.

Après avoir implémenté le code ci-dessous, exécutez-le simplement avec la commande python pour terminer la construction du serveur WebSocket!

ws_traffic_server.py


#! /usr/bin/env python
# -*- coding: utf-8 -*-
import tornado.ioloop
import tornado.web
import tornado.websocket
from tornado.options import define, options ,parse_command_line
from datetime import datetime
import shlex, subprocess , time ,re , json ,threading

SLEEP_TIME = 20 #Intervalle pour obtenir SNMP

COMMUNITY = 'dev'
IP = '127.0.0.1'
IFMIB_OID = '1.3.6.1.2.1.31.1.1.1'

snmp_command_in = 'snmpget -v 2c -c %s %s %s.6.2'%(COMMUNITY,IP,IFMIB_OID)
snmp_command_out = 'snmpget -v 2c -c %s %s %s.10.2'%(COMMUNITY,IP,IFMIB_OID)

#Spécifiez le port que WebSocket écoute
define("port", default = 8080,type = int)

class SendWebSocket(tornado.websocket.WebSocketHandler):

    #Événement appelé lorsqu'une connexion est sécurisée
    def open(self):
        print 'Session Opened. IP:' + self.request.remote_ip

    #Événements lorsqu'un événement de déconnexion se produit, par exemple lorsque le navigateur est fermé
    def on_close(self):
        print "Session closed"

    #Un événement appelé lorsqu'un message est envoyé par le client
    def on_message(self, message):
        if message == 'hello':
            pre_counter_in = int(self.exe_snmp(snmp_command_in))
            pre_counter_out = int(self.exe_snmp(snmp_command_out))

            #Heure sur WebSocket.la minuterie ne peut pas être utilisée. Reproduisez le délai avec la variable suivante CallBack
            #SLEE_Démarrer le traitement de la communication avec SNMP dans la seconde moitié après TIME secondes
            tornado.ioloop.IOLoop.instance().call_later(SLEEP_TIME,self.snmp_second_half,{'in_counter':pre_counter_in,'out_counter':pre_counter_out})

    #SLEEP_Obtenez SNMP après TIME secondes
    def snmp_second_half(self,pre_counters):
        result = {}
        pos_counter_in = int(self.exe_snmp(snmp_command_in))
        pos_counter_out = int(self.exe_snmp(snmp_command_out))

        #Calcul du trafic à partir de la différence du compteur par rapport au nombre de secondes spécifié
        traffic_in = (pos_counter_in - pre_counters['in_counter'])  / SLEEP_TIME
        traffic_out = (pos_counter_out - pre_counters['out_counter'])  / SLEEP_TIME
        #Envoyer des données de trafic converties en JSON à un client Web
        try:
            result.update({'traffic_in' : traffic_in , 'traffic_out' : traffic_out})
            result.update({'timestamp' : time.mktime(datetime.now().timetuple())})
            self.write_message(json.dumps(result))
        except:
            print "Client is already disconnectted."

    #Renvoie uniquement la valeur du résultat de l'exécution SNMP
    #e.g. [IF-MIB::ifHighSpeed.21 = Gauge32: 1000] -> [1000]
    def exe_snmp(self,snmp_command):
        split_command = shlex.split(snmp_command)
        exec_output = subprocess.check_output(split_command)
        r = re.compile("(.*)(: )(.*)")
        snmp_result = r.match(exec_output).group(3)
        return snmp_result

    #Accepte uniquement les communications des hôtes spécifiés comme non Vrai
    def check_origin(self, origin):
        return True

#Attend une demande de connexion à WS à l'URI spécifié
app = tornado.web.Application([
    (r"/ws/ifmon/", SendWebSocket),
])

if __name__ == "__main__":
    parse_command_line()
    app.listen(options.port)
    mainloop = tornado.ioloop.IOLoop.instance()
    mainloop.start() #Démarrez le serveur WebSocket

Ce qui est facile à acquérir ici, c'est le processus d'acquisition du trafic des périphériques réseau à partir de SNMP et de le calculer. Calcul du trafic par SNMP (IFcounter actuel --IFcounter après n secondes) / n Puisqu'il ne peut pas être calculé sans attendre plusieurs dizaines de secondes, le programme doit également attendre n secondes. Cependant, «time.sleep ()» dans la bibliothèque standard ne peut pas être utilisé pour une communication asynchrone telle que WebSocket. Étant donné que l'ensemble du processus est en veille, les autres téléspectateurs seront également affectés.

Cela a été résolu par une fonction dans tornado appelée ʻIOLoop.call_later () `qui peut rappeler après un délai.

Je pense que le programme est créé de force, donc j'apprécierais que vous puissiez commenter si vous devez l'écrire correctement.

  1. Mise en œuvre côté client = Le traitement côté client est effectué par Javascript. Le côté client doit connaître quatre gestionnaires d'événements afin de gérer les données de trafic envoyées par le serveur.
Event Moment de l'événement
onopen Événement appelé lorsque la connexion avec le serveur est établie
onmessage Événements qui se produisent lorsque des données sont reçues(Place principale)
onclose Événement appelé lorsque la connexion avec le serveur est perdue
onerror Événement appelé lorsqu'une erreur se produit

Lorsque la connexion au serveur est terminée et que l'événement «onopen» se produit, un signal «bonjour» est envoyé au serveur pour commencer à prendre SNMP. Lorsque vous envoyez «bonjour», le côté serveur réagit et envoie les données de trafic. Lorsque la réception de données du serveur est détectée, l'événement «message» est généré, de sorte que le graphique est dessiné dans cet événement. Si vous attrapez une action qui déconnecte le WebSocket, comme la fermeture du navigateur, l'événement ʻonclose` se produira et la communication s'arrêtera là.

Étant donné que les paramètres du graphique sont longs, je les omettrai. La partie pertinente du traitement WebSocket est implémentée comme suit.

ws_client.js


var WS_URL = 'ws://www5445uo.sakura.ne.jp:8080/ws/ifmon/'

var init_ws_Traffic = function(){
  var traffic_chart =Traffic_Chart('traffic_chart', {}); //Constructeur de graphes Highhart
  var ws = new WebSocket(WS_URL);
  console.log('----- WebSocket Open -----')

  ws.onopen = function() {//Établissement de la connexion WS
    ws.send('hello');
  };

  ws.onerror = function(event){
    $("p#error").text('Failed join to the server');
    console.log('Connection faild....')
  };

  ws.onmessage = function(event) { //Traitement des données de trafic
    var data = JSON.parse(event.data);
    var timestamp = data['timestamp']*1000 + 60 * 9 * 60 * 1000;
    var value = data['traffic_in']
    document.getElementById('traffic_in').innerHTML = convert_bps(value); //Afficher sous forme de texte
    traffic_chart.series[0].addPoint([timestamp, value], true, true); //L'ajout du graphique du trafic entrant se fait ici
    value = data['traffic_out']
    document.getElementById('traffic_out').innerHTML = convert_bps(value);
    traffic_chart.series[1].addPoint([timestamp, value], true, true);//L'ajout du graphique du trafic sortant se fait ici
    ws.send('hello'); //Prochaine demande de trafic
  };
  ws.onclose = function () {
    ws.send('close');
    ws.close();
  };
  window.onbeforeunload = function () {
    ws.send('close');
    ws.close()
  };
}

Les deux ci-dessus sont nécessaires pour la communication WebSocket. Si vous vérifiez ce que vous avez réellement fait sur l'écran Web, cela ressemblera à ceci. スクリーンショット 2015-12-11 1.19.25.png C'est un graphique ...!

Vous pouvez désormais obtenir un graphique du trafic toutes les 10 secondes pour travailler! Si vous faites quelque chose de similaire et avez une meilleure méthode, faites-le moi savoir.

  1. Bonus (démo et code source) = Je ne pense pas que ce soit bon sans le sentiment d'être mis à jour en temps réel, alors J'ai téléchargé ce que j'ai fait sur le serveur de démonstration. Cela peut être pour une durée limitée, mais regardez à quoi cela ressemble. Si vous attendez environ 20 secondes, quelque chose va changer.

** Il n'y a pas de démo en direct car le serveur d'installation de démo a disparu **

En outre, le code utilisé dans la démo est répertorié ci-dessous. https://github.com/Mabuchin/wstraffic

  1. Enfin = J'ai présenté comment implémenter un graphique de trafic pour une utilisation au travail! Je pense que ce sera plus pratique si vous le modifiez en fonction de votre objectif, par exemple en permettant de représenter le trafic de plusieurs appareils dans un graphique avec un peu de réécriture. Si vous avez une chance de l'utiliser, essayez-le!

Recommended Posts

J'ai essayé de décrire le trafic en temps réel avec WebSocket
J'ai essayé d'illustrer le temps et le temps du langage C
J'ai essayé de traiter l'image en "style croquis" avec OpenCV
J'ai essayé de traiter l'image dans un "style de dessin au crayon" avec OpenCV
J'ai essayé de résoudre le problème de F02 comment écrire en temps réel hors ligne avec Python
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé d'intégrer Keras dans TFv1.1
Comment écrire hors ligne en temps réel J'ai essayé de résoudre E11 avec python
J'ai essayé de classer les accords de guitare en temps réel en utilisant l'apprentissage automatique
Comment écrire en temps réel hors ligne J'ai essayé de résoudre E12 avec python
J'ai essayé d'entraîner la fonction péché avec chainer
J'ai essayé de représenter graphiquement les packages installés en Python
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé de prédire les chevaux qui seront dans le top 3 avec LightGBM
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai essayé d'analyser les émotions de tout le roman "Weather Child" ☔️
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de notifier les informations de retard de train avec LINE Notify
J'ai essayé de résumer le code souvent utilisé dans Pandas
J'ai essayé d'afficher l'heure et la météo d'aujourd'hui w
J'ai essayé de résumer les commandes souvent utilisées en entreprise
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
Je n'arrive pas à me connecter à la page d'administration avec Django 3
J'ai essayé de prédire le prix de l'immobilier à Boston avec PyCaret
J'ai essayé de créer un article dans Wiki.js avec SQL Alchemy
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
(Python: OpenCV) J'ai essayé de générer une valeur indiquant la distance entre les régions tout en binarisant la vidéo en temps réel.
J'ai essayé de publier automatiquement sur ChatWork au moment du déploiement avec Fabric et ChatWork Api
J'ai aussi essayé d'imiter la fonction monade et la monade d'état avec le générateur en Python
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
J'ai essayé de résoudre l'édition du débutant du livre des fourmis avec python
J'ai essayé d'automatiser l'arrosage du pot avec Raspberry Pi
J'ai essayé d'afficher le temps de lecture de la vidéo (OpenCV: version Python)
J'ai essayé de démarrer avec Bitcoin Systre le week-end
J'ai essayé d'agrandir la taille du volume logique avec LVM
Pour le moment, je veux convertir n'importe quel fichier avec ffmpeg !!
J'ai essayé d'améliorer l'efficacité du travail quotidien avec Python
J'ai essayé de me connecter automatiquement à Twitter avec du sélénium (RPA, scraping)
J'ai essayé de créer une fonction pour juger si les principaux stocks du monde sont l'heure d'été avec python
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé de résumer la commande umask
J'ai essayé d'implémenter la permutation en Python
J'ai essayé tensorflow pour la première fois
J'ai essayé de visualiser AutoEncoder avec TensorFlow
J'ai essayé de reconnaître le mot de réveil
J'ai essayé de commencer avec Hy
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé de créer un traitement par lots sans serveur pour la première fois avec DynamoDB et Step Functions
J'ai essayé de résumer la modélisation graphique.
J'ai essayé d'implémenter ADALINE en Python
J'ai essayé d'estimer le rapport de circonférence π de manière probabiliste
J'ai essayé de toucher l'API COTOHA
J'ai essayé d'implémenter PPO en Python
J'ai essayé d'implémenter CVAE avec PyTorch
J'ai essayé de jouer avec l'image avec Pillow