Skip to content

Commit 65e1d4a

Browse files
authored
Add files via upload
1 parent 9e5b9d2 commit 65e1d4a

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

Pure-FA_zabbix.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#!/usr/bin/python3
2+
3+
#
4+
## Overview
5+
#
6+
# This short Python example illustrates how to build a simple extension to the Zabbix agent to monitor
7+
# Pure Storage FlashArrays. The Pure Storage Python REST Client is used to query the FlashArray basic performance
8+
# counters. The typical sampling mechanism of Zabbix tends to acquire a single counter at time and this
9+
# somewhat discords with the queries that can be executed through the Python REST Client which
10+
# usualy gather multiple counters from a single request. Therefore, in order to avoid multiple call to the
11+
# same method to get different counters the script optimizes the number of calls by using a dedicated SQLite
12+
# database to store the results of a single query and limits the sample rate to the minimum interval of 60 seconds.
13+
#
14+
## Installation
15+
#
16+
# The script should be copied to a convenient folder location on the machine hosting the Zabbix agent meant to monitor
17+
# also the FlashArray, for example the /usr/local/bin folder.
18+
# Change the execution rights of the program to allow the execution to 'all' (usually chmod 0755).
19+
# Create a file named userparameter_pure.conf in the /etc/zabbix/zabbix_agentd.d/ folder. The file should
20+
# contain just the following line
21+
#
22+
# `UserParameter=pureFA.fainfo[*],/usr/local/bin/pure-FA.py $1 $2 $3 $4`
23+
#
24+
# Restart the Zabbix agent
25+
# Add a template to the Zabbix server configuration to model FlashArrays and populate the template with one application
26+
# containing the seven item representing the monitored counters: 'input_per_sec', 'output_per_sec', 'queue_depth', 'reads_per_sec',
27+
# 'writes_per_sec', 'usec_per_read_op' and 'usec_per_write_op'.
28+
# Each single item should have a Key specified like the following
29+
#
30+
# pureFA.fainfo[{$ENDPOINT},{$APITOKEN},{$CACHE}, *counter_name*]
31+
#
32+
# where *counter_name* is one of above mentioned counters.
33+
# Add to the template the graphs you may want to draw combininig the specified Items. The items can be used
34+
# to define Triggers as well.
35+
# Add each single FlashArray to the monitored hosts of the Zabbix server
36+
# Define the macros to be associated to the key variables {$ENDPOINT},{$APITOKEN} and{$CACHE}. ENDPOINT is
37+
# the hostname or the IP address of the Pure FlashArray, APITOKEN is the authentication token for that FlashArray
38+
# and CACHE is the local path of the SQLite database to store the temporary sampled counters.
39+
#
40+
#
41+
## Dependencies
42+
#
43+
# purestorage Pure Storage Python REST Client (https://github.com/purestorage/rest-client)
44+
# posix_ipc
45+
#
46+
47+
48+
49+
import argparse
50+
import sys
51+
import json
52+
import purestorage
53+
import urllib3
54+
import sqlite3
55+
import posix_ipc
56+
from dateutil.parser import parse
57+
from datetime import datetime, timedelta
58+
from time import sleep
59+
60+
# Global variables
61+
SEM_NAME = '/pure_sem_01'
62+
MIN_INTERVAL = 60
63+
TIMEOUT = 600
64+
65+
66+
def getFAinfo(flash_array, apitoken):
67+
# Retrieves the basic statistics of the given flash_array
68+
urllib3.disable_warnings()
69+
fa = purestorage.FlashArray(flash_array, api_token=apitoken)
70+
fainfo = fa.get(action='monitor')
71+
fa.invalidate_cookie()
72+
return(fainfo)
73+
74+
75+
parser = argparse.ArgumentParser(description="Retrieves basic FA info")
76+
parser.add_argument("endpoint", help="FA hostname or ip address")
77+
parser.add_argument("apitoken", help="FA api_token")
78+
parser.add_argument("cache", help="SQLite cache database")
79+
parser.add_argument("kpi", choices=['input_per_sec',
80+
'output_per_sec',
81+
'queue_depth',
82+
'reads_per_sec',
83+
'writes_per_sec',
84+
'usec_per_read_op',
85+
'usec_per_write_op'])
86+
args = parser.parse_args()
87+
88+
89+
conn = sqlite3.connect(args.cache, isolation_level=None)
90+
91+
# Get the most recent sampled data from db
92+
c = conn.cursor()
93+
c.execute("SELECT * FROM fainfo WHERE endpoint=?", (args.endpoint,))
94+
row = c.fetchone()
95+
fa_info = [{}];
96+
97+
if row is None:
98+
# table fainfo not initialized with this flasharray
99+
dt1 = datetime.min
100+
else:
101+
dt1 = parse(row[8])
102+
fa_info[0]['input_per_sec'] = row[1]
103+
fa_info[0]['output_per_sec'] = row[2]
104+
fa_info[0]['queue_depth'] = row[3]
105+
fa_info[0]['reads_per_sec'] = row[4]
106+
fa_info[0]['writes_per_sec'] = row[5]
107+
fa_info[0]['usec_per_read_op'] = row[6]
108+
fa_info[0]['usec_per_write_op'] = row[7]
109+
fa_info[0]['time'] = row[8]
110+
111+
# Check if last sample is too old.
112+
dt2 = datetime.now(dt1.tzinfo)
113+
d = dt2 - dt1
114+
115+
if (d.total_seconds() > MIN_INTERVAL):
116+
# Last
117+
sem = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value = 1)
118+
if (d.total_seconds() > TIMEOUT):
119+
sem.release()
120+
sem.unlink()
121+
sem = posix_ipc.Semaphore(SEM_NAME, posix_ipc.O_CREAT, initial_value = 1)
122+
123+
try:
124+
# Attempt to enter critical section. This is necessary in order to have a single writer
125+
# accessing the sqlite database at time
126+
sem.acquire(0)
127+
# Get basic statistics from FlashArray
128+
fa_info = getFAinfo(args.endpoint, args.apitoken)
129+
130+
if row is None:
131+
# No entry for this FlashArrayA. Prepare the statement to add the record for this FlashArray
132+
sql = ''' INSERT INTO fainfo(
133+
in_per_sec,
134+
out_per_sec,
135+
q_depth,
136+
rd_per_sec,
137+
wr_per_sec,
138+
rd_latency,
139+
wr_latency,
140+
time,
141+
endpoint)
142+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'''
143+
else:
144+
# Prepare the statement to update the record for this FlashArray.
145+
sql = ''' UPDATE fainfo
146+
SET in_per_sec = ? ,
147+
out_per_sec = ? ,
148+
q_depth = ? ,
149+
rd_per_sec = ? ,
150+
wr_per_sec = ? ,
151+
rd_latency = ? ,
152+
wr_latency = ? ,
153+
time = ?
154+
WHERE endpoint = ?'''
155+
156+
# Execute the SQL query
157+
c.execute(sql, (fa_info[0]['input_per_sec'],
158+
fa_info[0]['output_per_sec'],
159+
fa_info[0]['queue_depth'],
160+
fa_info[0]['reads_per_sec'],
161+
fa_info[0]['writes_per_sec'],
162+
fa_info[0]['usec_per_read_op'],
163+
fa_info[0]['usec_per_write_op'],
164+
fa_info[0]['time'],
165+
args.endpoint))
166+
sem.release()
167+
except posix_ipc.BusyError:
168+
pass
169+
170+
# Print to standard outout, This returns the value to the Zabbix agent
171+
print(fa_info[0][args.kpi])
172+
conn.close()
173+
sys.exit(0)

0 commit comments

Comments
 (0)