386 lines
18 KiB
Python
386 lines
18 KiB
Python
import re
|
|
|
|
from KRParser import patterns, keyrequests, helper
|
|
from enum import Flag, auto
|
|
import threading
|
|
import concurrent.futures
|
|
from jsonmerge import merge
|
|
import pandas as pd
|
|
from tqdm import *
|
|
|
|
|
|
|
|
class KROption(Flag):
|
|
VALIDATE_EXISTS = auto()
|
|
VALIDATE_HASDATA = auto()
|
|
RESOLVEKEYREQUETS = auto()
|
|
RESOLVESERVICES = auto()
|
|
|
|
class KRParser:
|
|
patterns=[patterns.Pattern1(), patterns.Pattern2(), patterns.Pattern3(), patterns.Pattern5(), patterns.Pattern4(), patterns.Pattern5()]
|
|
lock = threading.Lock()
|
|
|
|
def normalize(self,x):
|
|
tmp=x.replace("\n","")
|
|
tmp=tmp.replace("/\"","")
|
|
tmp=tmp.replace("\"","")
|
|
tmp=tmp.replace("\t","")
|
|
tmp=re.sub("([\s]*)\)", ")", tmp)
|
|
tmp=re.sub("\([\s\n\r]*", "(", tmp)
|
|
tmp=re.sub("\,[\s\n\r]*", ",", tmp)
|
|
tmp=re.sub("\)[\s\n\r]*,", "),", tmp)
|
|
tmp=re.sub("in[\s\n\r]*\(", "in(", tmp)
|
|
|
|
return tmp
|
|
|
|
|
|
def applyPatterns(self,subject):
|
|
groups=None
|
|
for p in self.patterns:
|
|
groups=p.parseServicesAndMethods(subject)
|
|
|
|
if len(groups) > 0:
|
|
for g in groups:
|
|
g["pattern"]=str(p.__class__)
|
|
break
|
|
|
|
return groups
|
|
|
|
|
|
def checkKeyRequetsHasData(self,kr, tfrom, DTAPIURL, DTAPIToken):
|
|
|
|
# DTAPIURL = DTAPIURL + "/api/v2/entities"
|
|
|
|
# headers = {
|
|
# 'Content-Type': 'application/json',
|
|
# 'Authorization': 'Api-Token ' + DTAPIToken
|
|
# }
|
|
|
|
# for gid, group in enumerate(kr.matchedGroups):
|
|
# params={"entitySelector": group["existsQuery"], "from":tfrom["tfrom"], "fields": "fromRelationships"}
|
|
# response = helper.get_request(DTAPIURL, headers, params)
|
|
# entities = (response.json())['entities']
|
|
|
|
# if len(entities) > 0:
|
|
# y=0
|
|
# for method in kr.keyRequests:
|
|
# if method["groupId"] == gid:
|
|
# found = [x for x in entities if x[method["comparer"]] == method[method["comparer"]]]
|
|
|
|
# if len(found) > 0:
|
|
# method["hasData"][tfrom["label"]]=True
|
|
# else:
|
|
# method["hasData"][tfrom["label"]]=False
|
|
|
|
DTAPIURL = DTAPIURL + "/api/v2/metrics/query"
|
|
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Api-Token ' + DTAPIToken
|
|
}
|
|
|
|
for gid, group in enumerate(kr.matchedGroups):
|
|
params={"entitySelector": group["existsQuery"], "resolution":"1d", "metricSelector": "builtin:service.keyRequest.count.total", "from":tfrom["tfrom"]}
|
|
response = helper.get_request(DTAPIURL, headers, params)
|
|
entities = (response.json())["result"][0]["data"]
|
|
|
|
for method in kr.keyRequests:
|
|
if method["groupId"] == gid:
|
|
|
|
found = [x for x in entities if x["dimensions"][0] == method["entityId"]]
|
|
|
|
if len(found) > 0:
|
|
method["hasData"][tfrom["label"]]=True
|
|
method["count"][tfrom["label"]]=sum([x for x in found[0]['values'] if x != None])
|
|
else:
|
|
method["hasData"][tfrom["label"]]=False
|
|
method["count"][tfrom["label"]]=0
|
|
|
|
def resolveServices(self,services, DTAPIURL, DTAPIToken):
|
|
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Api-Token ' + DTAPIToken
|
|
}
|
|
|
|
for gid, service in enumerate(services):
|
|
query="type(SERVICE),entityId("+service["id"]+")"
|
|
|
|
params=merge(self.config["serviceLookupParams"],{"entitySelector": query})
|
|
|
|
response = helper.get_request(DTAPIURL, headers, params)
|
|
entities = (response.json())['entities']
|
|
|
|
if len(entities)>0:
|
|
services[gid]=entities[0]
|
|
|
|
|
|
|
|
def resolveKeyRequests(self,kr, DTAPIURL, DTAPIToken, options):
|
|
DTAPIURL = DTAPIURL + "/api/v2/entities"
|
|
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Api-Token ' + DTAPIToken
|
|
}
|
|
|
|
tmp_KR=[]
|
|
for gid, group in enumerate(kr.matchedGroups):
|
|
#try:
|
|
for k in group["methods"]:
|
|
|
|
|
|
#tmp_kr={"displayName":None, "entityId":None, "groupId":gid, "hasData":{}, "count":{},"services":[], "found":False, "foundCount":0, "exception":""}
|
|
|
|
query="type(service_method)"
|
|
|
|
if len(group["services"])> 0:
|
|
|
|
if group["services"][0].startswith("SERVICE-"):
|
|
query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityId(\""+'","'.join(group["services"])+"\"))"
|
|
else:
|
|
query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityName.in(\""+'","'.join(group["services"])+"\"))"
|
|
|
|
if k.startswith("SERVICE_METHOD-"):
|
|
query+=",entityId("+k+")"
|
|
else:
|
|
query+=",entityName.in(\""+k+"\")"
|
|
|
|
params={"entitySelector": query, "from":"now-1y","fields": "fromRelationships"}
|
|
response = helper.get_request(DTAPIURL, headers, params)
|
|
entities = (response.json())['entities']
|
|
|
|
if len(entities)> 0:
|
|
for ent in entities:
|
|
|
|
tmp_kr=merge({"displayName":ent["displayName"], "entityId":ent["entityId"], "groupId":gid, "hasData":{}, "count":{},"services":[], "found":False, "foundCount":0, "exception":"", "_mInSloDef": group["_mInSloDef"] }, self.config["extendResultObjects"])
|
|
|
|
if "isServiceMethodOfService" in ent["fromRelationships"]:
|
|
tmp_kr["services"]=ent["fromRelationships"]["isServiceMethodOfService"]
|
|
|
|
if options and KROption.RESOLVESERVICES in options and len(tmp_kr["services"])>0:
|
|
self.resolveServices(tmp_kr["services"], DTAPIURL, DTAPIToken)
|
|
|
|
tmp_KR.append(tmp_kr)
|
|
|
|
else:
|
|
if k.startswith('SERVICE_METHOD-'):
|
|
tmp_kr=merge({"displayName": None,"comparer": "entityId", "entityId":k, "groupId":gid, "hasData":{},"count":{},"services":[], "found":False, "foundCount":0, "exception":"", "_mInSloDef": group["_mInSloDef"]}, self.config["extendResultObjects"]) #"exists":None, 'hasData_1W':None,
|
|
else:
|
|
tmp_kr=merge({"displayName":k,"comparer": "displayName", "entityId":None, "groupId":gid, "hasData":{}, "count":{},"services":[], "found":False, "foundCount":0, "exception":"", "_mInSloDef": group["_mInSloDef"]}, self.config["extendResultObjects"]) #"exists":None, 'hasData_1W':None,
|
|
|
|
tmp_KR.append(tmp_kr)
|
|
|
|
|
|
return tmp_KR
|
|
# except Exception as err:
|
|
# kr.keyRequests[gid]["exception"]="resolveKeyRequests failed: "+repr(err)
|
|
|
|
# for gid, k in enumerate(kr.keyRequests):
|
|
# try:
|
|
# query="type(service_method)"
|
|
# group=kr.matchedGroups[k["groupId"]]
|
|
|
|
# if len(group["services"])> 0:
|
|
# if group["services"][0].startswith("SERVICE-"):
|
|
# query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityId(\""+'","'.join(group["services"])+"\"))"
|
|
# else:
|
|
# query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityName.in(\""+'","'.join(group["services"])+"\"))"
|
|
|
|
# if k["comparer"]=="entityId":
|
|
# query+=",entityId("+k["entityId"]+")"
|
|
# else:
|
|
# query+=",entityName.in(\""+k["displayName"]+"\")"
|
|
|
|
# params={"entitySelector": query, "from":"now-1y","fields": "fromRelationships"}
|
|
# response = helper.get_request(DTAPIURL, headers, params)
|
|
# entities = (response.json())['entities']
|
|
|
|
|
|
# if len(entities)> 0:
|
|
|
|
# kr.keyRequests[gid]["found"]=True
|
|
# kr.keyRequests[gid]['foundCount']=len(entities)
|
|
# kr.keyRequests[gid]["displayName"]=entities[0]["displayName"]
|
|
# kr.keyRequests[gid]["entityId"]=entities[0]["entityId"]
|
|
|
|
# if "isServiceMethodOfService" in entities[0]["fromRelationships"]:
|
|
# kr.keyRequests[gid]["services"]=entities[0]["fromRelationships"]["isServiceMethodOfService"]
|
|
|
|
# if options and KROption.RESOLVESERVICES in options and len( kr.keyRequests[gid]["services"])>0:
|
|
# self.resolveServices(kr.keyRequests[gid]["services"], DTAPIURL, DTAPIToken)
|
|
|
|
# except Exception as err:
|
|
# kr.keyRequests[gid]["exception"]="resolveKeyRequests failed: "+repr(err)
|
|
|
|
|
|
|
|
#kr.mergeServices(entities)
|
|
|
|
# def resolveKeyRequests(self,kr, DTAPIURL, DTAPIToken, options):
|
|
# DTAPIURL = DTAPIURL + "/api/v2/entities"
|
|
|
|
# headers = {
|
|
# 'Content-Type': 'application/json',
|
|
# 'Authorization': 'Api-Token ' + DTAPIToken
|
|
# }
|
|
|
|
|
|
# for gid, k in enumerate(kr.keyRequests):
|
|
# try:
|
|
# query="type(service_method)"
|
|
# group=kr.matchedGroups[k["groupId"]]
|
|
|
|
# if len(group["services"])> 0:
|
|
# if group["services"][0].startswith("SERVICE-"):
|
|
# query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityId(\""+'","'.join(group["services"])+"\"))"
|
|
# else:
|
|
# query+=",fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityName.in(\""+'","'.join(group["services"])+"\"))"
|
|
|
|
# if k["comparer"]=="entityId":
|
|
# query+=",entityId("+k["entityId"]+")"
|
|
# else:
|
|
# query+=",entityName.in(\""+k["displayName"]+"\")"
|
|
|
|
# params={"entitySelector": query, "from":"now-1y","fields": "fromRelationships"}
|
|
# response = helper.get_request(DTAPIURL, headers, params)
|
|
# entities = (response.json())['entities']
|
|
|
|
|
|
# if len(entities)> 0:
|
|
|
|
# kr.keyRequests[gid]["found"]=True
|
|
# kr.keyRequests[gid]['foundCount']=len(entities)
|
|
# kr.keyRequests[gid]["displayName"]=entities[0]["displayName"]
|
|
# kr.keyRequests[gid]["entityId"]=entities[0]["entityId"]
|
|
|
|
# if "isServiceMethodOfService" in entities[0]["fromRelationships"]:
|
|
# kr.keyRequests[gid]["services"]=entities[0]["fromRelationships"]["isServiceMethodOfService"]
|
|
|
|
# if options and KROption.RESOLVESERVICES in options and len( kr.keyRequests[gid]["services"])>0:
|
|
# self.resolveServices(kr.keyRequests[gid]["services"], DTAPIURL, DTAPIToken)
|
|
|
|
# except Exception as err:
|
|
# kr.keyRequests[gid]["exception"]="resolveKeyRequests failed: "+repr(err)
|
|
|
|
|
|
|
|
# #kr.mergeServices(entities)
|
|
|
|
|
|
def getKeyRequestsByServices(self, services):
|
|
#type(SERVICE_METHOD),fromRelationship.isServiceMethodOfService(type("SERVICE"),entityName.in("btc-user-composite-service - PROD"))
|
|
DTAPIURL = self.DTAPIURL + "/api/v2/entities"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Api-Token ' + self.DTAPIToken
|
|
}
|
|
|
|
if len(services) > 0:
|
|
if services[0].startswith("SERVICE-"):
|
|
query="type(service_method),fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityId(\""+'","'.join(services)+"\"))"
|
|
else:
|
|
query="type(service_method),fromRelationship.isServiceMethodOfService(type(\"SERVICE\"),entityName.in(\""+'","'.join(services)+"\"))"
|
|
|
|
params={"entitySelector": query,"from":"now-1y"}
|
|
response = helper.get_request(DTAPIURL, headers, params)
|
|
entities = (response.json())['entities']
|
|
|
|
return entities
|
|
|
|
|
|
def process(self, kr):
|
|
|
|
for gid, group in enumerate(kr.matchedGroups):
|
|
|
|
if len(group["services"]) > 0 and len(group["methods"])==0:
|
|
tmp_methods=self.getKeyRequestsByServices(group["services"])
|
|
|
|
for m in tmp_methods:
|
|
group["methods"].append(m["entityId"])
|
|
|
|
# tmp=merge({"displayName": None,"comparer": "entityId", "entityId":m["entityId"], "groupId":gid, "hasData":{}, "count":{},"services":[], "found":False, "foundCount":0, "exception":""},self.config["extendResultObjects"]) #"exists":None, 'hasData_1W':None,
|
|
# kr.keyRequests.append(tmp)
|
|
|
|
# for method in group["methods"]:
|
|
# if method.startswith('SERVICE_METHOD-'):
|
|
# tmp=merge({"displayName": None,"comparer": "entityId", "entityId":method, "groupId":gid, "hasData":{},"count":{},"services":[], "found":False, "foundCount":0, "exception":""}, self.config["extendResultObjects"]) #"exists":None, 'hasData_1W':None,
|
|
# else:
|
|
# tmp=merge({"displayName":method,"comparer": "displayName", "entityId":None, "groupId":gid, "hasData":{}, "count":{},"services":[], "found":False, "foundCount":0, "exception":""}, self.config["extendResultObjects"]) #"exists":None, 'hasData_1W':None,
|
|
|
|
# kr.keyRequests.append(tmp)
|
|
|
|
|
|
|
|
kr.keyRequests=self.resolveKeyRequests(kr,self.DTAPIURL, self.DTAPIToken, self.options)
|
|
|
|
if self.options:
|
|
|
|
if KROption.VALIDATE_HASDATA in self.options:
|
|
self.checkKeyRequetsHasData(kr,{"label":"1W", "tfrom":"now-1w"},self.DTAPIURL, self.DTAPIToken)
|
|
self.checkKeyRequetsHasData(kr,{"label":"1M", "tfrom":"now-1M"},self.DTAPIURL, self.DTAPIToken)
|
|
|
|
return kr
|
|
|
|
|
|
def parseBySLO(self,row):
|
|
|
|
try:
|
|
normFilter=self.normalize(row['filter'])
|
|
normExpresseion=self.normalize(row['metricExpression'])
|
|
|
|
tmp_KR = keyrequests.KR(merge({"sloName":row["name"], "sloId":row["id"], "metricExpression": normExpresseion, "filter": normFilter, "matchedGroups": None}, self.config["extendResultObjects"]))
|
|
|
|
#SLO with Filter
|
|
if normFilter.upper().startswith("TYPE(SERVICE_METHOD),") or normFilter.upper().startswith("TYPE(SERVICE),"):
|
|
subject=normFilter
|
|
else:
|
|
subject=normExpresseion
|
|
|
|
groups=self.applyPatterns(subject)
|
|
|
|
tmp_KR.matchedGroups.append(groups)
|
|
|
|
kr=self.process(tmp_KR)
|
|
|
|
with self.lock:
|
|
self.krs.append(kr)
|
|
self.pbar.update()
|
|
|
|
except Exception as err:
|
|
print(repr(err))
|
|
|
|
def parse(self, input):
|
|
|
|
with concurrent.futures.ThreadPoolExecutor(self.config["threads"]) as executor:
|
|
|
|
if type(input) == pd.DataFrame:
|
|
self.pbar = tqdm(total=input["id"].count(),desc=self.name)
|
|
|
|
for index, row in input.iterrows():
|
|
executor.submit(self.parseBySLO, row)
|
|
|
|
elif type(input)== list:
|
|
self.pbar = tqdm(total=len(input), desc=self.name)
|
|
for slo in input:
|
|
executor.submit(self.parseBySLO, slo)
|
|
|
|
elif type(input) == dict:
|
|
#self.pbar = tqdm(total=1, desc=self.name)
|
|
#executor.submit(self.parseBySLO, slo)
|
|
self.parseBySLO(input)
|
|
|
|
|
|
return self.krs
|
|
|
|
|
|
|
|
def __init__(self, name="Default Parser", options: KROption=None ,config={}, DTAPIURL=None, DTAPIToken=None ):
|
|
self.name=name
|
|
self.DTAPIURL= DTAPIURL
|
|
self.DTAPIToken=DTAPIToken
|
|
self.options=options
|
|
self.config=merge({"threads": 3,
|
|
"serviceLookupParams":{"from":"now-2y"},
|
|
"extendResultObjects":{}}, config)
|
|
self.krs=[] |