Skip to content

Commit ad4d6df

Browse files
authored
Initial upload
1 parent 6d475a6 commit ad4d6df

File tree

5 files changed

+2054
-0
lines changed

5 files changed

+2054
-0
lines changed

queryHandler/Query.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
'''
2+
##############################################################################
3+
# Copyright 2019 IBM Corp.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
##############################################################################
17+
18+
Created on Feb 4, 2017
19+
20+
@author: NSCHULD
21+
'''
22+
#
23+
import sys
24+
25+
26+
def isString(val):
27+
if sys.version < '3':
28+
return isinstance(val, basestring)
29+
else:
30+
return isinstance(val, str)
31+
32+
33+
class Query(object):
34+
'''
35+
Updated Version of a Zimon Query Interface for metric queries only
36+
Allows construction most queries with the Ctor. Also adds a fluent API for other cases
37+
'''
38+
39+
# OPERATION TYPES
40+
NOP = 0 # No operation (metric only)
41+
SUM = 1 # sum operation
42+
AVG = 2 # avg operation
43+
MAX = 3 # max operation
44+
MIN = 4 # min operation
45+
RATE = 5 # rate operation
46+
OPERATIONS = {
47+
NOP: "{0}", SUM: "sum({0})", AVG: "avg({0})", MAX: "max({0})", MIN: "min({0})", RATE: "rate({0})"}
48+
OPS_STR = {'nop': NOP, 'noop': NOP, 'sum': SUM, 'avg': AVG, 'max': MAX, 'min': MIN, 'rate': RATE}
49+
50+
FIELDS = set(["gpfs_cluster_name", "gpfs_disk_name", "gpfs_diskpool_name",
51+
"gpfs_disk_usage_name", "gpfs_fset_name", "gpfs_fs_name",
52+
"mountPoint", "netdev_name", 'diskdev_name', "node", "db_name",
53+
"operation", "protocol", "waiters_time_threshold", "export",
54+
"nodegroup", "account", "filesystem", "tct_csap", "tct_operation", "cloud_nodeclass"])
55+
56+
def __init__(self, metrics=None, bucketsize=1, filters=None, groupby=None, includeDiskDate=False):
57+
'''
58+
Constructor, filters and groupby must be preformmated
59+
'''
60+
self.includeDiskData = includeDiskDate # disk or archived data (False or True)
61+
self.bucket_size = bucketsize # bucket size
62+
63+
self.metrics = [] # list of string metrics
64+
self.filters = [] # list of filters
65+
self.groupby = [] # list of groupBy metrics
66+
67+
if metrics is not None:
68+
if (isString(metrics)):
69+
self.metrics = metrics.split(',')
70+
elif iter(metrics) is not iter(metrics):
71+
self.metrics = metrics
72+
else:
73+
raise ValueError("metrics are not in the right format")
74+
75+
if filters is not None:
76+
if (isString(filters)):
77+
self.filters = filters.split(',')
78+
elif iter(filters) is not iter(filters):
79+
self.filters = filters
80+
else:
81+
raise ValueError("filters are not in the right format")
82+
83+
if groupby is not None:
84+
if (isString(groupby)):
85+
self.groupby = groupby.split(',')
86+
elif iter(groupby) is not iter(groupby):
87+
self.groupby = groupby
88+
else:
89+
raise ValueError("groupby are not in the right format")
90+
91+
self.timeRep = ' now' # string time representation
92+
self.measurements = {}
93+
self.normalize_rates = True
94+
self.key = None
95+
self.sensor = None
96+
97+
def addMetric(self, metric, op=None):
98+
'''
99+
Add metric operation to the query, e.g., "sum(cpu_idle)" using the input
100+
metric name and operation type
101+
'''
102+
if isString(op):
103+
op = Query.OPS_STR.get(op.lower(), Query.NOP)
104+
105+
metricop = Query.OPERATIONS.get(op, '{0}').format(metric)
106+
self.metrics.append(metricop)
107+
return self
108+
109+
def addKey(self, key):
110+
self.key = key
111+
112+
def addMetricsGroup(self, sensor):
113+
self.sensor = sensor
114+
115+
def addGroupByMetric(self, groupByMetric):
116+
'''Add a metric to be used in grouping multi-metric (operation) columns'''
117+
if groupByMetric not in self.FIELDS:
118+
raise ValueError("unknown groupby type %s" % groupByMetric)
119+
self.groupby.append(groupByMetric)
120+
return self
121+
122+
def addFilter(self, field, value):
123+
'''Add a filter of the form "field=value" where
124+
the field is an identifier key element and
125+
value is a constant or a regular expression'''
126+
if field not in self.FIELDS:
127+
raise ValueError("unknown filter type %s" % field)
128+
newFilter = field + "=" + value
129+
if newFilter not in self.filters:
130+
self.filters.append(newFilter)
131+
return self
132+
133+
def setBucketSize(self, bucketsize):
134+
self.bucket_size = bucketsize
135+
return self
136+
137+
def setTime(self, tstart='', tend='', num_buckets=0, duration=0):
138+
'''
139+
Specify time bounds
140+
'''
141+
self.timeRep = 'now' # default
142+
143+
if tstart:
144+
self.timeRep = "tstart " + tstart
145+
if tend:
146+
if not tstart:
147+
self.timeRep = ''
148+
self.timeRep += " tend " + tend
149+
if num_buckets:
150+
self.timeRep = "last " + str(num_buckets)
151+
if duration:
152+
self.timeRep = "duration " + str(duration)
153+
return self
154+
155+
def addComputation(self, name, prg):
156+
'''add a named, derived column to the resultset
157+
:param name: name used for the colum in the reultset
158+
:param prg: comma delimited list of steps to take,
159+
step can be metric name, operation or number (UPN notation)
160+
161+
'''
162+
self.measurements[name] = prg.split(',')
163+
return self
164+
165+
def addRatio(self, metric1, metric2, op=NOP):
166+
'''
167+
Add ratio computation
168+
'''
169+
metricop1 = Query.OPERATIONS.get(op, '{0}').format(metric1)
170+
metricop2 = Query.OPERATIONS.get(op, '{0}').format(metric2)
171+
if metricop1 not in self.metrics:
172+
self.metrics.append(metricop1)
173+
if metricop2 not in self.metrics:
174+
self.metrics.append(metricop2)
175+
self.addComputation(metricop1 + '/' + metricop2, metric1 + ',' + metric2 + ',/')
176+
return self
177+
178+
def addMeasurement(self, meassure):
179+
'''
180+
Add a pre-defined measurement
181+
'''
182+
self.metrics.extend(meassure.metrics)
183+
self.groupby.extend(meassure.groupby)
184+
self.filters.extend(meassure.filters)
185+
self.measurements.update(meassure.measurements)
186+
return self
187+
188+
def __str__(self):
189+
dd = '-a' if self.includeDiskData else ''
190+
191+
if self.sensor is not None:
192+
queryString = 'get -j {0} group {1} bucket_size {2} {3}'.format(
193+
dd, self.sensor, self.bucket_size, self.timeRep)
194+
elif self.key is not None:
195+
queryString = 'get -j {0} {1} bucket_size {2} {3}'.format(
196+
dd, self.key, self.bucket_size, self.timeRep)
197+
else:
198+
queryString = 'get -j {0} metrics {1} bucket_size {2} {3}'.format(
199+
dd, ','.join(self.metrics), self.bucket_size, self.timeRep)
200+
201+
if self.filters:
202+
queryString += ' from ' + ",".join(self.filters)
203+
204+
if self.groupby:
205+
queryString += ' group_by ' + ','.join(self.groupby)
206+
queryString += '\n'
207+
return queryString
208+
209+
210+
class Measurement(Query):
211+
'''Just a marker class'''
212+
213+
def __init__(self, *args, **kwargs):
214+
super(self.__class__, self).__init__(*args, **kwargs)

0 commit comments

Comments
 (0)