|
| 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