Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Fdall committed Nov 16, 2018
1 parent d72fe05 commit 7a0d576
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 1 deletion.
161 changes: 161 additions & 0 deletions glpi/wip/glpi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import requests, json, glpi_logger

# TODO clean initalizing to create the adequate ticket

# Class used to create the php filter to look for objects in the glpi api
class criterias:
def __init__(self):
self.array = []

def addCriteria(self, criteria):
self.array.append(criteria)

def __str__(self):
output = ""
for iCriteria in range(len(self.array)):
output += (self.array[iCriteria]).show(iCriteria) + "&"
return output[:len(output)-1]

class criteria:
def __init__(self, field, searchType, value, link="AND"):
self.link = link
self.field = field
self.searchtype = searchType
self.value = value

def show(self, i):
sep = "criteria[" + str(i) + "]"
return sep + "[link]=" + self.link + "&" + sep + "[field]=" + self.field + "&" + sep + "[searchtype]=" + self.searchtype + "&" + sep + "[value]=" + self.value



# Class used to create a ticket in the glpi api
class glpiTicket:
def __init__(self, **fields):
self.__dict__ = fields

def __init__(self, name, content, status=1, urgency=3, impact=3, priority=3):
self.name = name
self.content = content
self.status = status
self.urgency = urgency
self.impact = impact
self.priority = priority
# self.entities_id = entities_id
# self.requesttypes_id = requesttypes_id
# self.itilcategories_id = itilcategories_id
# self.type = type
# self.global_validation = global_validation

def build(self):
return { key:value for key, value in self.__dict__.items() if not key.startswith('__') and not callable(key) }

class glpiSession:
def __init__(self, userToken, apiToken, baseUrl):
self.userToken = userToken
self.apiToken = apiToken
self.baseUrl = baseUrl
self.headers = ''
self.sessionToken = ''
self.field = {
"content": "21",
"status": "12",
"name": "1",
"type": "14"
}
self.status = {
"assigned": "2",
"planned": "3",
"pending": "4",
"solved": "5",
"closed": "6",
"new": "1"
}

def initSession(self):
headers = { 'Content-Type': 'application/json', 'Authorization': "user_token "+self.userToken, 'App-Token': self.apiToken }
response = requests.get(self.baseUrl + '/initSession', headers=headers)
if (self.controlCode(['200'], response)):
self.sessionToken = response.json()['session_token']
self.headers = { 'Content-Type': 'application/json', 'Session-Token': self.sessionToken, 'App-Token': self.apiToken }

def killSession(self):
response = requests.get(self.baseUrl + '/killSession', headers=self.headers)
self.controlCode(['200'], response)
exit()

def openTicket(self, name, description):
data = { "input": glpiTicket(name, description).build() }
response = requests.post(self.baseUrl + '/Ticket' , headers=self.headers, data=json.dumps(data))
if (self.controlCode(['201'], response)):
return 0

def getTickets(self):
response = requests.get(self.baseUrl + '/Ticket' , headers=self.headers)
if (self.controlCode(['200'], response)):
return response.json()

def getTicket(self, ticketID):
response = requests.get(self.baseUrl + '/Ticket/' + ticketID , headers=self.headers)
if (self.controlCode(['200'], response)):
return response.json()

def searchTicket(self, name, content=None, ticket_status=None):
filters = criterias()
# Filter on Name
nameCriteria = criteria(self.field['name'], "contains", name, link="AND")
filters.addCriteria(nameCriteria)
# Filter on Description
if (content != None):
contentCriteria = criteria(self.field['content'], "contains", content, link="AND")
filters.addCriteria(contentCriteria)
# Filter on ticket_status
if (ticket_status != None):
statusCriteria = criteria(self.field['status'], "equals", self.status[ticket_status], link="AND")
filters.addCriteria(statusCriteria)

response = requests.get(self.baseUrl + '/search/Ticket?' + str(filters), headers=self.headers)
if (self.controlCode(['200'], response)):
return response.json()

def similarTicketExists(self, ticket, ticket_status=None):
if (ticket_status != None):
for iStatus in ticket_status:
if (self.searchTicket(ticket.name, ticket.content, iStatus)['totalcount'] >= 1):
return True
return False
else:
if (self.searchTicket(ticket.name, ticket.content, ticket_status)['totalcount'] >= 1):
return True
else:
return False

def customGETRequest(self, url):
response = requests.get(self.baseUrl + url , headers=self.headers)
if (self.controlCode(['200'], response)):
return (response.text.encode('utf-8'))

def controlCode(self, expectedCodes, response):
if (str(response.status_code) in expectedCodes):
return True
else:
print(response.text.encode('utf-8'))
print("Something went wrong while calling the GLPI API, received error code " + str(response.status_code))
self.killSession()
return False



#userToken="2XVW2uSJXHlADythfFKmJzHlNYu5JSvcBsI4UoUB"
#apiToken="2IaJezu1IfuPFAjfMTV0ypqFFjiKXyhROnaoMNp0"
#a = glpiSession(userToken, apiToken, "http://192.168.180.99/glpi/apirest.php")
#a.initSession()
#print(json.dumps(a.getTickets()))
#print(a.customGETRequest("/listSearchOptions/Ticket"))
#ticket = glpiTicket("test8", "seg")
#a.similarTicketExists(ticket)
#print(json.dumps(a.searchTicket("[Rudder]test command")))
##a.getTickets()
##a.openTicket("test1", "description1")


6 changes: 6 additions & 0 deletions glpi/wip/glpi_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
LOG_FILE = "/root/fda/notify.log"

def log(message):
f = open(LOG_FILE, "a")
f.write(message)
f.close()
152 changes: 152 additions & 0 deletions glpi/wip/notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import requests, urllib3, json, configparser, log_tail, os, re, time, glpi, traceback, glpi_logger

class NotifyWorker:

def __init__(self, pipefile, conf):
self.fifo_pipe = pipefile
self.conf = conf
self.notif_queue = []
self.timestamp = int(time.time())
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
self.glpi = None
try:
os.mkfifo(self.fifo_pipe)
except OSError as e:
if str(e).find("File exists") < 0:
raise

# This function reads non-compliance data from a pipe.
# It is meant to be used with the Rudder server non-compliance hooks.
def run(self):
while True:
if self.timestamp % 60 == 0: # This will check if there are unsent mails in the queue, and send them if the batch period is elapsed
self.notify_mail()
with open(self.fifo_pipe) as pipe:
self.handle_non_compliance(pipe.read())

# This function reads its input from the Rudder server non-compliance logs directly.
# It is a temporary solution, used until the server's non-compliance hooks are implemented.
def run_with_logtail(self):
for line in log_tail.log_tail("/var/log/rudder/compliance/non-compliant-reports.log"):
self.handle_non_compliance(line)

def start(self):
#self.run()
glpi_logger.log("Starting watch watch\n")
self.run_with_logtail()

def handle_non_compliance(self, msg_str):
try:
msg = Message(msg_str)
glpi_logger.log("parsing line: " + msg_str + "\n")
self.notif_queue.append(msg)
if self.conf["MAIL"]["on"] == "true":
self.notify_mail()
if self.conf["SLACK"]["on"] == "true":
self.notify_slack(msg)
if self.conf["GLPI"]["on"] == "true":
self.notify_glpi(msg)
except Exception as e:
glpi_logger.log("/!\ something went wrong:" + str(e))
glpi_logger.log(traceback.print_exc())


def notify_slack(self, msg):
for webhook in self.conf["SLACK"]["webhooks"].split(' '):
requests.post(webhook, headers={"Content-type":"application/json"}, data=json.dumps({
"text":"*RUDDER NON-COMPLIANCE*\n" + str(msg)
}))

def notify_glpi(self, msg):
glpi_logger.log(" -- notify via glpi --\n")
if (self.glpi == None):
userToken="2XVW2uSJXHlADythfFKmJzHlNYu5JSvcBsI4UoUB"
apiToken="2IaJezu1IfuPFAjfMTV0ypqFFjiKXyhROnaoMNp0"
newSession = glpi.glpiSession(userToken, apiToken, "http://192.168.180.99/glpi/apirest.php")
glpi_logger.log(" -- initializing session --\n")
newSession.initSession()
glpi_logger.log(" -- done --\n")
self.glpi = newSession

# Do not parse repaired reports or logs
if (msg.getResultStatus() != "result_repaired" and msg.getResultStatus() != "log_repaired" and msg.getResultStatus() != "log_warn"):
for iFile in self.conf['GLPI']['files'].split(' '):
ticketName = "[Rudder]" + msg.data['directive_name']
ticketContent = str(msg)
lookedContent = msg.withoutTimeStamp()
lookedStatus = ['new', 'assigned', 'planned', 'pending']

glpi_logger.log("looking for ticket: " + ticketName + "\n with content" + lookedContent + "\n")
sampleTicket = glpi.glpiTicket(ticketName, lookedContent)
if not self.glpi.similarTicketExists(sampleTicket, lookedStatus):
glpi_logger.log(" -- creating ticket --")
self.glpi.openTicket(ticketName, ticketContent)
glpi_logger.log(" -- done --\n\n")
else:
glpi_logger.log(" -- SKIPPING, ticket already exists --\n\n")
else:
glpi_logger.log(" -- SKIPPING, repaired report --\n\n")

def notify_mail(self):
if self.conf["MAIL"]["nospam"] == "true" and time.time() - self.timestamp < self.get_mail_batch_period():
return
# The code above is enabling batch mail-sending by checking if the configured period has elapsed since last mail

tmp_mailfile = "/tmp/rudder-notify-mail.txt"
with open(tmp_mailfile, 'a') as out:
out.write(str(len(self.notif_queue)) + " notification" + ('s' if len(self.notif_queue) > 1 else '') + " from Rudder :\n")
for i in range(0, len(self.notif_queue)):
out.write("# " + str(i+1) + ' :\n' + str(self.notif_queue[i]) + '\n')
self.notif_queue = []
for recipient in self.conf["MAIL"]["recipients"].split(' '):
os.system("mail -s 'Rudder non-compliance notification' " + recipient + " < " + tmp_mailfile)
os.remove(tmp_mailfile)
self.timestamp = time.time() # And we reset the timestamp

def get_mail_batch_period(self):
regex = re.compile("([0-9]*)d?([0-9]*)h?([0-9]*)m?")
g = map(int, regex.search(self.conf["MAIL"]["batch_period"]).groups())
return 86400 * g[0] + 3600 * g[1] + 60 * g[2]

class Message:

def __init__(self, msg):
regex = re.compile("^\[(?P<Date>[^\]]+)\] N: (?P<NodeUUID>[^ ]+) \[(?P<NodeFQDN>[^\]]+)\] S: \[(?P<Result>[^\]]+)\] R: (?P<RuleUUID>[^ ]+) \[(?P<RuleName>[^\]]+)\] D: (?P<DirectiveUUID>[^ ]+) \[(?P<DirectiveName>[^\]]+)\] T: (?P<TechniqueName>[^/]+)/(?P<TechniqueVersion>[^ ]+) C: \[(?P<ComponentName>[^\]]+)\] V: \[(?P<ComponentKey>[^\]]+)\] (?P<Message>.+)$")
groups = regex.search(msg).groups()
self.data = {
"date": groups[0],
"node_uuid": groups[1],
"node_fqdn": groups[2],
"result": groups[3],
"rule_uuid": groups[4],
"rule_name": groups[5],
"directive_uuid": groups[6],
"directive_name": groups[7],
"technique_name": groups[8],
"technique_version": groups[9],
"component_name": groups[10],
"component_key": groups[11],
"message": groups[12]
}

def __str__(self):
return " - Date: " + self.data["date"] + \
"\n - Node UUID: " + self.data["node_uuid"] + \
"\n - Node FQDN: " + self.data["node_fqdn"] + \
"\n - Result: " + self.data["result"] + \
"\n - Rule UUID: " + self.data["rule_uuid"] + \
"\n - Rule name: " + self.data["rule_name"] + \
"\n - Directive UUID: " + self.data["directive_uuid"] + \
"\n - Directive name: " + self.data["directive_name"] + \
"\n - Technique name: " + self.data["technique_name"] + \
"\n - Technique version: " + self.data["technique_version"] + \
"\n - Component name: " + self.data["component_name"] + \
"\n - Component key: " + self.data["component_key"] + \
"\n - Message: " + self.data["message"] + "\n"

def withoutTimeStamp(self):
return self.__str__().split("\n")[1]

def getResultStatus(self):
return self.data['result']

Loading

0 comments on commit 7a0d576

Please sign in to comment.