SolarWinds & Jira & Integration for Infrastructure Monitoring using Python, SQL, REST API

Author: aashutosh

Updated: 17 Feb 2023 11:51 a.m.

Tags: #Python #Database #Scripting #REST #API #Automation #SQL #Jira

Summary : SolarWinds, a monitoring application, collects monitoring parameters from target objects, a Python Script reads new alerts from SolarWinds Database, and create and assign a Jira ticket

SolarWinds Application collects all Alerts about any Objects in Different Tables in SolarWinds Database. The Alerts are stored in some tables only when the Alerts are Active, when Alert is resolved, Alerts is moved out of Active Alerts Table and recorded in Historical Tables. In Order to integrate SolarWinds Monitoring in Jira Service Desk for Ticket Creation for each alert, we:

 

Read Active Alerts from SolarWinds Database

 

Insert Active Alerts to INTERMEDIATE DB Active_Alerts Table.

 

Process Alerts

 

 

Sample Ticket in Jira Created by Script.

 

Python Script to integrate Solarwinds Alert to Jira.

import json, pyodbc, requests, threading, pandas as pd, time, os
from requests.auth import HTTPBasicAuth
tic = time.perf_counter() 

#Jira Connection
url = "https://<jira-instance-url>/rest/api/2/issue/"
auth = HTTPBasicAuth("<jira_user>", "jira_user_password")
headers = { "Accept": "application/json","Content-Type": "application/json"}

#Solarwinds Database Connection
server = '<DB-INSTANCE-NAME>'         #DB Instance where Script will store Data
database = '<DB-NAME>'                 #DB Name where Script will store Data
username = '<DB-USERNAME>' 
password = '*********' 
con = 'Yes'
conn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
#conn.autocommit = True
cursor = conn.cursor()

def create_ticket(ai,name,ec,rnc,ltdt,sev,ot,ia,ct,cy,r,ag,o):
    """ This Function Create a Ticket in Jira Service Desk """
    cursor = conn.cursor()
    Alert_ID=ai
    summary = rnc + ' ' + name
    desc = name + '\n' + ec + '\n' + rnc
    desc = "Alert: {} \n Entity: {} \n Node: {}".format(name,ec,rnc)
    desc = f"*Alert: {name}*\n*Node: {rnc}*\n*Alert_Object: {ec}*\n*Object_Type: {ot}*\nIP_Address: {ia}\nTrigger Time: {ltdt} (UTC)\nCity: {ct}\nCountry: {cy}\nRegion: {r}\nAssignment_Group: {ag}\nOwners: {o}"
    if sev == 1:
        v_priority = "High"
    elif sev == 2:
        v_priority = "Medium"
    else:
        v_priority = "Low"
    payload = json.dumps(
        {
            "fields": 
            {
            "project":
                {
                "key": "<PROJECT-KEY>"
                },
            "summary": summary,
            "description": desc,
            "issuetype": 
                {
                "name": "Incident",
                },
            "components":
                [{"name":"Active Directory" }],
            "priority":
                {"name":v_priority},
            "assignee":
                {
                "name":"aashutosh.kumar@technicolor.com"
                }
           }
        }
    )
    response = requests.request(
    "POST",
    url,
    data=payload,
    headers=headers,
    auth=auth,
    verify=r'bundle.pem'
    )
    print('\nTicket Created:')
    print('Alert ID: ',ai,'\n',json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))    
    Ticket_No = json.loads(response.text)['key']
    update_ticket_table_sql = """insert into jira_test.Ticket_Log values ({},'{}','OPEN');commit;""".format(Alert_ID,Ticket_No)
    #print(update_ticket_table_sql)
    cursor.execute(update_ticket_table_sql)
    cursor.commit()
    cursor.close()


def resolve_ticket(ticket):
    """This Function Resolves Jira SD Ticket for Resolved Alerts"""
    cursor = conn.cursor()
    comment_url='<jira-instance-url>/rest/api/2/issue/' + ticket + '/comment'
    comment_payload = json.dumps(
        {
        "body":"Issue Detected Resolved by Solarwinds"
        }
    )
    response = requests.request("POST",comment_url,data=comment_payload,headers=headers,auth=auth,verify=r'bundle.pem')      
    url='<jira-instance-url>/rest/api/2/issue/'+ticket+'/transitions'
    payload = json.dumps(
        {
        "update": {},
        "fields": 
            {
            "resolution": 
                {
                "name": "Done"
                }
            },
        "transition":
            {
            "id": "111"
            }
        }
        )
    response = requests.request(
        "POST",
        url,
        data=payload,
        headers=headers,
        auth=auth,
        verify=r'bundle.pem'
        )
    update_ticket_table_sql = "update <DB-NAME>.ticket_log set Ticket_Status='RESOLVED' where Ticket_No='{}';".format(ticket)
    #print(update_ticket_table_sql)
    cursor.execute(update_ticket_table_sql)
    cursor.commit()
    cursor.close()
    if len(response.text) == 0:
        print(f'\n\tTicket Resolved:{ticket}\n')
    else:
        print(f'\n\tError in Updating : {ticket}')
        print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))

fetch_alerts="""
drop table <DB-NAME>.jira_test.Active_Alerts;
select
    AA.AlertActiveID as AlertActiveID,
    AO.AlertObjectID as AlertObjectID,AHV.Name as Name, AO.EntityCaption as EntityCaption, AO.RelatedNodeCaption as RelatedNodeCaption,AO.LastTriggeredDateTime,
    AC.ObjectType as ObjectType,AHV.Severity as Severity, 
    N.NodeID ,N.IP_Address, N.Comments ,N.City, N.Country ,N.Region ,N.Assignment_Group ,N.Owners
    into JIRA_SW_TEST.jira_test.Active_Alerts
from
    [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.AlertObjects AO
    INNER JOIN     [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.AlertActive AA ON AO.AlertObjectID    =    AA.AlertObjectID
    INNER JOIN  [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.AlertHistoryView AHV ON AA.AlertActiveID    =    AHV.AlertActiveID         
                                                                                                        AND AO.AlertObjectID    =    AHV.AlertObjectID
    INNER JOIN     [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.AlertConfigurations AC ON AC.AlertID            =    AO.AlertID
    INNER JOIN     [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.NodesCustomProperties NCP ON AO.RelatedNodeID     =    NCP.NodeID
    INNER JOIN     [<Solarwinds-DB-Server-Instance>].solarwindsorion.dbo.Nodes N ON AO.RelatedNodeID     =     N.NodeID
where 
    AHV.EventTypeWord='Triggered'
and upper(AHV.Name) not like '%HIGH RESPONSE TIME%' 
and upper(AHV.Name) not like '%HIGH PACKET LOSS%'
Order By AO.LastTriggeredDateTime desc;
commit;    
"""

process_alerts="""
DECLARE @NEWVALUE int
DECLARE @RESOLVED int
select @NEWVALUE = count(*) from jira_test.Active_Alerts where NOT EXISTS 
(select 1 from jira_test.Alert_log where jira_test.Active_Alerts.AlertActiveID = jira_test.Alert_log.AlertActiveID)
select @RESOLVED = count(*) from jira_test.Alert_log where NOT EXISTS 
(select 1 from jira_test.Active_Alerts where jira_test.Active_Alerts.AlertActiveID = jira_test.Alert_log.AlertActiveID)
IF (@NEWVALUE > 0)
BEGIN
    insert into jira_test.Alert_log select *,'NEW' from jira_test.Active_Alerts where NOT EXISTS 
    (select 1 from jira_test.Alert_log where jira_test.Active_Alerts.AlertActiveID = jira_test.Alert_log.AlertActiveID);
    commit;
END
IF (@RESOLVED > 0)
BEGIN
    update jira_test.Alert_log set Alert_Status = 'RESOLVED' where 
    AlertActiveID in (select AlertActiveID from jira_test.Alert_log where NOT EXISTS 
    (select 1 from jira_test.Active_Alerts where jira_test.Active_Alerts.AlertActiveID = jira_test.Alert_log.AlertActiveID));
    commit;
END 
"""

cursor.execute(fetch_alerts)
cursor.execute(process_alerts)
cursor.commit()
cursor.close()

#Create Ticket for New Alerts
new_alerts = pd.read_sql_query('SELECT * FROM JIRA_SW_TEST.jira_test.Alert_log where ALert_Status=\'NEW\' and  AlertActiveID not in (select Alert_ID from jira_test.Ticket_log) ',conn)
data = new_alerts[['AlertActiveID','Name','EntityCaption','RelatedNodeCaption','LastTriggeredDateTime','Severity','ObjectType','IP_Address','City','Country','Region','Assignment_Group','Owners']]
for index,row in data.iterrows():
    threading.Thread(target=create_ticket,args=[ row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12] ]).start()

#Resolve Ticket for Resolved Alerts
resolved_alerts = pd.read_sql_query('select Ticket_No from jira_test.Ticket_Log TL left join jira_test.Alert_log AL on TL.Alert_ID = AL.AlertActiveID where TL.Ticket_Status=\'OPEN\' and AL.Alert_Status=\'RESOLVED\'',conn)
for index, row in resolved_alerts.iterrows():
    threading.Thread(target=resolve_ticket,args=[row[0]]).start()
toc = time.perf_counter() 
print(f"\nDone!    Time Taken= {toc-tic:0.4f} Seconds\n")