This commit is contained in:
Adrien Bourmault 2022-02-08 20:25:51 +01:00
parent 0a88053e0c
commit 775906f43e
No known key found for this signature in database
GPG Key ID: 6EB408FE0ACEC664
6 changed files with 236 additions and 80 deletions

6
bot.py
View File

@ -1,5 +1,6 @@
import slixmpp import slixmpp
import logging import logging
import random
from systemd.journal import JournalHandler from systemd.journal import JournalHandler
from hintTables import annecdotetable, topictable from hintTables import annecdotetable, topictable
@ -9,7 +10,6 @@ from commands import commandtable
# Logging # Logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.addHandler(JournalHandler()) log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
class MUCBot(slixmpp.ClientXMPP): class MUCBot(slixmpp.ClientXMPP):
@ -43,7 +43,7 @@ class MUCBot(slixmpp.ClientXMPP):
# any presences you send yourself. To limit event handling # any presences you send yourself. To limit event handling
# to a single room, use the events muc::room@server::presence, # to a single room, use the events muc::room@server::presence,
# muc::room@server::got_online, or muc::room@server::got_offline. # muc::room@server::got_online, or muc::room@server::got_offline.
self.add_event_handler("muc::%s::got_online" % self.room, self.add_event_handler("muc::%s::got_online" % self.rooms[0],
self.muc_online) self.muc_online)
@ -135,7 +135,7 @@ class MUCBot(slixmpp.ClientXMPP):
the user's nickname the user's nickname
""" """
if presence['muc']['nick'] in self.datastore.maintainerData: if presence['muc']['nick'] in self.datastore.knownMaintainers:
self.send_message(mto=presence['from'].bare, self.send_message(mto=presence['from'].bare,
mbody="Salut %s, vos services ont produit des " + mbody="Salut %s, vos services ont produit des " +
"alertes en votre absence !\nUtilisez la commande"+ "alertes en votre absence !\nUtilisez la commande"+

View File

@ -2,29 +2,30 @@ import pickle
import logging import logging
from systemd.journal import JournalHandler from systemd.journal import JournalHandler
from data import HostData
# Logging # Logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.addHandler(JournalHandler()) log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
# Isengard commands # Isengard commands
def cmdping(owners, nick, text, sbuf): def cmdping(owners, nick, text, store):
""" """
Ping command. Ping command.
""" """
return "pong !" return "pong !"
def cmdhelp(owners, nick, text, sbuf): def cmdhelp(owners, nick, text, store):
""" """
Ping command. Ping command.
""" """
global commandtable global commandtable
msg = "Je suis Isengard, le bot de supervision de ce salon.\n" msg = "je suis Isengard, le bot de supervision de ce salon.\n"
msg += "\n" msg += "\n"
msg += "Voici la liste de mes commandes disponibles : \n" msg += "Voici la liste de mes commandes disponibles : \n"
@ -33,7 +34,7 @@ def cmdhelp(owners, nick, text, sbuf):
return msg return msg
def cmdmainteneur(owners, nick, text, sbuf): def cmdmainteneur(owners, nick, text, store):
""" """
Change maintainer for an host Change maintainer for an host
""" """
@ -49,31 +50,29 @@ def cmdmainteneur(owners, nick, text, sbuf):
host = splittedtext[1] host = splittedtext[1]
return "le responsable de cette machine est " \ return "le responsable de cette machine est " \
+ sbuf.serviceData[host]["maintainer"] + store.knownHosts[host]["maintainer"]
if len(splittedtext) == 3: if len(splittedtext) == 3:
host = splittedtext[1] host = splittedtext[1]
maintainer = splittedtext[2] maintainer = splittedtext[2]
sbuf.serviceData[host] = {} if not host in store.knownHosts:
sbuf.serviceData[host]["destmuc"] = None store.knownHosts[host] = HostData(host)
sbuf.serviceData[host]["status_state"] = None
sbuf.serviceData[host]["status_type"] = None
sbuf.serviceData[host]["text"] = None
sbuf.serviceData[host]["raw"] = None
sbuf.serviceData[host]["needUpdate"] = False
sbuf.serviceData[host]["maintainer"] = maintainer
return "le responsable est à présent " \ store.knownHosts[host].maintainer = maintainer
+ sbuf.serviceData[host]["maintainer"] return "le responsable est à présent " + maintainer
except KeyError as e:
log.error(repr(e))
return "machine inconnue (tout le monde est son mainteneur)"
except Exception as e: except Exception as e:
print(repr(e)) log.error(repr(e))
return "Erreur à l'exécution" return "erreur à l'exécution"
return "Syntaxe invalide" return "Syntaxe invalide"
def cmdsave(owners, nick, text, sbuf): def cmdsave(owners, nick, text, store):
""" """
Save Save
""" """
@ -83,14 +82,14 @@ def cmdsave(owners, nick, text, sbuf):
try: try:
with open('current_buffer', 'wb') as current_buffer_file: with open('current_buffer', 'wb') as current_buffer_file:
pickle.dump(sbuf.serviceData, current_buffer_file) pickle.dump(store.knownHosts, current_buffer_file)
return "OK" return "OK"
except Exception as e: except Exception as e:
print(repr(e)) log.error(repr(e))
return "Erreur à l'exécution" return "erreur à l'exécution"
def cmdload(owners, nick, text, sbuf): def cmdload(owners, nick, text, store):
""" """
Save Save
""" """
@ -100,29 +99,34 @@ def cmdload(owners, nick, text, sbuf):
try: try:
with open('current_buffer', 'rb') as current_buffer_file: with open('current_buffer', 'rb') as current_buffer_file:
sbuf.serviceData = pickle.load(current_buffer_file) store.knownHosts = pickle.load(current_buffer_file)
return "OK" return "OK"
except Exception as e: except Exception as e:
print(repr(e)) log.error(repr(e))
return "Erreur à l'exécution" return "erreur à l'exécution"
def cmdhist(owners, nick, text, sbuf): def cmdhist(owners, nick, text, store):
""" """
Check history for a maintainer Check history for a maintainer
""" """
if not nick in sbuf.maintainerData or len(sbuf.maintainerData[nick]) == 0: if not nick in store.knownMaintainers or len(store.knownMaintainers[nick]) == 0:
return "pas d'historique disponible pour vous." return "pas d'historique disponible pour vous."
msg = "voici historique pour le mainteneur %s (vous) :\n" % nick msg = "voici les cinq derniers évènements pour chaque hôte notifié:\n"
for host in sbuf.maintainerData[nick]: for host in store.knownMaintainers[nick]:
msg += "\nHôte %s:" % host msg += "\nHôte %s:" % host
for serviceline in sbuf.maintainerData[nick][host]: count = 0
for serviceline in store.knownMaintainers[nick][host]:
msg += "- %s" % serviceline + "\n" msg += "- %s" % serviceline + "\n"
count += 1
if count >= 5:
break
sbuf.maintainerData[nick] = {} del store.knownMaintainers[nick]
store.knownMaintainers[nick] = {}
return msg return msg
# Commands # Commands

BIN
current_buffer Normal file

Binary file not shown.

195
data.py
View File

@ -7,7 +7,7 @@ from systemd.journal import JournalHandler
# Logging # Logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.addHandler(JournalHandler()) log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
class HostData: class HostData:
""" """
@ -17,21 +17,19 @@ class HostData:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
# Concerning services
self.serviceLastType = ""
self.serviceLastStatus = ""
self.serviceLastText = ""
# Concerning host # Concerning host
self.lastType = "" self.type = ""
self.lastStatus = "" self.status = "OK"
# Concerning services
self.statuses = {}
self.types = {}
# Tools # Tools
self.linkedMUC = "" self.counts = {"CRITICAL":0, "WARNING":0, "OK":0}
self.notifCount = 0
self.needUpdate = False
self.maintainer = "Tout le monde" self.maintainer = "Tout le monde"
class DataStore: class DataStore:
def __init__(self, linkedBot): def __init__(self, linkedBot):
@ -43,29 +41,172 @@ class DataStore:
self.linkedBot = linkedBot self.linkedBot = linkedBot
def push(self, msg): def push(self, msg):
"""
# TYPE|HOST/SERVICE|STATE|OUTPUT|SENDER|COMMENT Process messages like TYPE|HOST/SERVICE|STATE|OUTPUT|SENDER|COMMENT
"""
# Get current time # Get current time
curtime = datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S") curtime = datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S")
# Get all params # Get all params
destmuc, status_type, location, status_state, text, sender, comment = msg.split("|") destmuc, type, location, status, text, sender, comment = msg.split("|")
print("Dest: %s, Msg: %s" % (destmuc, msg)) # Check if message is about a service or host
print("Status: %s" % (status_type + " (" + status_state + ")")) try:
# check if message is about a service or host
if len(location.split("/")) > 1:
host, service = location.split("/") host, service = location.split("/")
except ValueError:
host = location.split("/")[0]
service = ''
# Create raw text from notification
raw = "%s [%s/%s]: %s (%s %s)" % (curtime, type, status, text,
sender, comment)
cur = None
log.info("Datastore received: %s" % msg)
log.info("Datastore understood: %s" % raw)
# Look for host
if host in self.knownHosts:
cur = self.knownHosts[host]
# It's a service
if service != "":
# is it known ?
if service in cur.statuses:
# does the status change ?
if cur.statuses[service] != status:
# update
if not cur.statuses[service] in cur.counts:
cur.counts[status] = 0
cur.counts[cur.statuses[service]] -= 1
if not status in cur.counts:
cur.counts[status] = 0
cur.counts[status] += 1
cur.statuses[service] = status
cur.types[service] = status
else: else:
host = location # create status entry
service = False cur.statuses[service] = status
print("Host: %s, Service: %s" % (host, service)) if not status in cur.counts:
cur.counts[status] = 0
print("Text: %s" % (text)) cur.counts[status] += 1
# It's not a service (so general)
else:
# does the status change ?
if cur.status != status:
cur.status = status
cur.type = type
# Host is not known
else:
# create host
self.knownHosts[host] = HostData(host)
cur = self.knownHosts[host]
# It's a service
if service != "":
# create status entry
cur.statuses[service] = status
if not status in cur.counts:
cur.counts[status] = 0
cur.counts[status] += 1
# It's not a service (so general)
else:
# create status entry
cur.status = status
cur.type = type
# Update history
if not cur.maintainer in self.knownMaintainers:
self.knownMaintainers[cur.maintainer] = {}
if not host in self.knownMaintainers[cur.maintainer]:
self.knownMaintainers[cur.maintainer][host] = []
self.knownMaintainers[cur.maintainer][host].append(raw)
# Is there only one service or more problems for this host?
problemCount = 0
for cstatus in cur.counts:
if not "OK" in cstatus:
problemCount += cur.counts[cstatus]
# If this notification is a problem
if not "OK" in status:
# General problem
if not "OK" in cur.status and problemCount == 0:
message = "je détecte un problème général (%s)" \
" sur %s (%s)" % (status, host, text)
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
# Only one service has a problem
elif service and problemCount == 1:
message = "je détecte un problème (%s) sur le service %s de" \
" la machine %s" \
" (%s)" % (status, service, host, text)
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
# Multiple problems
else:
message = "je détecte de multiples problèmes " \
"sur la machine %s\n" % (host)
# create recap from statuses that are not OK
for cstatus in cur.counts:
if not "OK" in cstatus:
message += "%s %s(s), " % \
(str(cur.counts[cstatus]), cstatus)
message = message[:-2]
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
# We have a recovery
else:
# General problem
if not service and problemCount == 0:
message = "fin d'alerte générale sur" \
" sur %s (%s)" % (host, text)
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
# Only one service has a problem
elif service and problemCount == 0:
message = "résolution du problème sur le service %s de" \
" la machine %s" \
" (%s)\n" % (service, host, text)
# create recap from statuses that are not OK
for cstatus in cur.counts:
message += "%s %s(s), " % \
(str(cur.counts[cstatus]), cstatus)
message = message[:-2]
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
# Resolution but multiple problems
else:
message = "résolution d'alertes en cours " \
"sur la machine %s\n" % (host)
# create recap from statuses
for cstatus in cur.counts:
message += "%s %s(s), " % \
(str(cur.counts[cstatus]), cstatus)
message = message[:-2]
# send notification
log.info("Sending to %s: %s" % (destmuc, message))
self.linkedBot.push(destmuc, cur.maintainer+", "+message)
log.info("Datastore known hosts: %s" % str(self.knownHosts))
log.info("Datastore known maintainers: %s" % str(self.knownHosts))

View File

@ -7,7 +7,6 @@ from systemd.journal import JournalHandler
# Logging # Logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.addHandler(JournalHandler()) log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
class LocalServer(threading.Thread): class LocalServer(threading.Thread):
@ -19,23 +18,23 @@ class LocalServer(threading.Thread):
def run(self): def run(self):
sockfd = socket.socket() sockfd = socket.socket()
print("Socket successfully created") log.info("Socket successfully created")
port = 12346 # Reserve a port on your computer...in our case it is 12345, but it can be anything port = 12346 # Reserve a port on your computer...in our case it is 12345, but it can be anything
sockfd.bind(('127.0.0.1', port)) sockfd.bind(('127.0.0.1', port))
print("Socket binded to %s" %(port)) log.info("Socket binded to %s" %(port))
sockfd.listen(5) # Put the socket into listening mode sockfd.listen(5) # Put the socket into listening mode
print("Socket is listening") log.info("Socket is listening")
while not self.pleaseStop: while not self.pleaseStop:
client, addr = sockfd.accept() # Establish connection with client client, addr = sockfd.accept() # Establish connection with client
print('Got connection from', addr) log.debug('Got connection from', addr)
rcvStr = client.recv(1024).decode() rcvStr = client.recv(1024).decode()
print(rcvStr) log.debug(rcvStr)
if rcvStr != '': if rcvStr != '':
self.sharedBuffer.push(rcvStr) self.sharedBuffer.push(rcvStr)
client.send(b'') # Send a message to the client client.send(b'') # Send a message to the client
client.close() client.close()
print("Socket is listening") log.debug("Socket is listening")
print("Socket is closed\nExiting") log.info("Socket is closed\nExiting")

30
main.py
View File

@ -14,12 +14,6 @@ from bot import MUCBot
from data import DataStore from data import DataStore
from localServer import LocalServer from localServer import LocalServer
# Logging
log = logging.getLogger(__name__)
log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
if __name__ == '__main__': if __name__ == '__main__':
def cleanExit(): def cleanExit():
@ -58,8 +52,8 @@ if __name__ == '__main__':
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", parser.add_argument("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-r", "--room", dest="room", parser.add_argument("-r", "--rooms", dest="rooms",
help="MUC room to join") help="MUC rooms to join separated by comma")
parser.add_argument("-n", "--nick", dest="nick", parser.add_argument("-n", "--nick", dest="nick",
help="MUC nickname") help="MUC nickname")
@ -68,11 +62,29 @@ if __name__ == '__main__':
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
log = logging.getLogger(__name__)
log.addHandler(JournalHandler())
# Setup the MUCBot and register plugins. Note that while plugins may # Setup the MUCBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = MUCBot(args.jid, args.password, args.room, args.nick)
jid = args.jid
password = args.password
rooms = args.rooms
nick = args.nick
if not jid:
jid = "isengard@a-lec.org"
if not password:
password = "iLw62WF3kfvt4j"
if not rooms:
rooms = "tests-isengard@salons.a-lec.org"
if not nick:
nick = "Isengard"
xmpp = MUCBot(jid, password, rooms.split(","), nick)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat xmpp.register_plugin('xep_0045') # Multi-User Chat
xmpp.register_plugin('xep_0199') # XMPP Ping xmpp.register_plugin('xep_0199') # XMPP Ping