Skip to content

Commit f33b649

Browse files
authored
Merge pull request #246 from Helene/backport_important_fixes_to_v7
Backport important fixes to v7.2
2 parents fdad184 + 8270840 commit f33b649

File tree

6 files changed

+119
-58
lines changed

6 files changed

+119
-58
lines changed

source/metadata.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from queryHandler.Topo import Topo
2727
from queryHandler import SensorConfig
2828
from utils import execution_time
29-
from messages import MSG
29+
from messages import ERR, MSG
3030
from metaclasses import Singleton
3131
from time import sleep
3232

@@ -129,14 +129,15 @@ def __initializeTables(self):
129129
raise ValueError(MSG['NoSensorConfigData'])
130130
MAX_ATTEMPTS_COUNT = 3
131131
for attempt in range(1, MAX_ATTEMPTS_COUNT + 1):
132-
self.__metaData = Topo(self.qh.getTopology())
133-
if not (self.metaData and self.metaData.topo):
132+
topoStr = self.qh.getTopology()
133+
if not topoStr:
134134
if attempt > MAX_ATTEMPTS_COUNT:
135135
break
136136
# if no data returned because of the REST HTTP server is still starting, sleep and retry (max 3 times)
137137
self.logger.warning(MSG['NoDataStartNextAttempt'].format(attempt, MAX_ATTEMPTS_COUNT))
138138
sleep(self.sleepTime)
139139
else:
140+
self.__metaData = Topo(topoStr)
140141
foundItems = len(self.metaData.allParents) - 1
141142
sensors = self.metaData.sensorsSpec.keys()
142143
self.logger.info(MSG['MetaSuccess'])
@@ -155,10 +156,11 @@ def update(self, refresh_all=False):
155156
if refresh_all:
156157
self.__sensorsConf = SensorConfig.readSensorsConfigFromMMSDRFS(self.logger)
157158

158-
self.__metaData = Topo(self.qh.getTopology())
159-
if not (self.metaData and self.metaData.topo):
159+
topoStr = self.qh.getTopology()
160+
if not topoStr:
160161
self.logger.error(MSG['NoData']) # Please check the pmcollector is properly configured and running.
161-
raise cherrypy.HTTPError(404, MSG[404])
162+
raise cherrypy.HTTPError(404, ERR[404])
163+
self.__metaData = Topo(topoStr)
162164
self.logger.details(MSG['MetaSuccess'])
163165
self.logger.debug(MSG['ReceivAttrValues'].format('parents', ", ".join(self.metaData.allParents)))
164166
return ({'msg': MSG['MetaSuccess']})

source/opentsdb.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,8 @@ def parse_result_tags(self, identifiersMap):
352352
if identifiersMap:
353353
for identifiers in identifiersMap:
354354
d = defaultdict(dict)
355-
for key in identifiers.keys():
356-
d['tags'][key] = identifiers[key]
357-
if d not in self.results:
358-
self.results.append(d)
355+
d['tags'] = identifiers
356+
self.results.append(d)
359357

360358

361359
class SingleTimeSeriesResponse():

source/queryHandler/PerfmonRESTclient.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
@author: HWASSMAN
2121
'''
2222

23+
import urllib.request
2324
# catch import failure on AIX since we will not be shipping our third-party libraries on AIX
2425
try:
2526
import requests
@@ -32,6 +33,22 @@
3233
DEFAULT_HEADERS = {"Accept": "application/json",
3334
"Content-type": "application/json"}
3435

36+
session = None
37+
38+
39+
def get_session():
40+
global session
41+
if not session:
42+
session = requests.Session()
43+
return session
44+
45+
46+
def close_session():
47+
global session
48+
if session:
49+
session.close()
50+
session = None
51+
3552

3653
def getAuthHandler(keyName, keyValue):
3754
if not isinstance(keyName, bytes):
@@ -73,27 +90,39 @@ class perfHTTPrequestHelper(object):
7390
2. waits until the server response, finally forwards a result to the QueryHandler
7491
"""
7592

76-
def __init__(self, logger, reqdata=None, session=None):
77-
self.session = session or requests.Session()
93+
def __init__(self, logger, reqdata=None, caCert=False):
94+
self.session = get_session()
95+
self.caCert = caCert
7896
self.requestData = reqdata
7997
self.logger = logger
8098

8199
def doRequest(self):
82100
if self.requestData and isinstance(self.requestData, requests.Request):
101+
self.session.verify = self.caCert
83102
_prepRequest = self.session.prepare_request(self.requestData)
84103
try:
85104
res = self.session.send(_prepRequest)
86105
return res
106+
except requests.exceptions.ProxyError:
107+
close_session()
108+
self.logger.debug(f"doRequest __ ProxyError. Found configured proxies: {urllib.request.getproxies()}")
109+
res = requests.Response()
110+
res.status_code = 503
111+
res.reason = "Unable to connect to proxy"
112+
return res
87113
except requests.exceptions.ConnectionError:
114+
close_session()
88115
res = requests.Response()
89116
res.status_code = 503
90117
res.reason = "Connection refused from server"
91118
return res
92119
except requests.exceptions.RequestException as e:
120+
close_session()
93121
self.logger.debug('doRequest __ RequestException. Request data: {}, Response data: {}'.format(e.request, e.response))
94122
res = requests.Response()
95123
res.status_code = 404
96124
res.reason = "The request could not be processed from server"
97125
return res
98126
else:
127+
close_session()
99128
raise TypeError('doRequest __ Error: request data wrong format')

source/queryHandler/Query.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class Query(object):
4747
"gpfs_disk_usage_name", "gpfs_fset_name", "gpfs_fs_name",
4848
"mountPoint", "netdev_name", 'diskdev_name', "node", "db_name",
4949
"operation", "protocol", "waiters_time_threshold", "export",
50-
"nodegroup", "account", "filesystem", "tct_csap", "tct_operation", "cloud_nodeclass"])
50+
"nodegroup", "account", "filesystem", "tct_csap", "tct_operation", "cloud_nodeclass",
51+
"gpfs_health_component", "gpfs_health_entity"])
5152

5253
DISK_CAP_METRICS = set(["gpfs_disk_disksize", "gpfs_disk_free_fullkb", "gpfs_disk_free_fragkb",
5354
"gpfs_pool_disksize", "gpfs_pool_free_fragkb", "gpfs_pool_free_fullkb",
@@ -91,6 +92,7 @@ def __init__(self, metrics=None, bucketsize=1, filters=None, groupby=None, inclu
9192
self.timeRep = ' now' # string time representation
9293
self.measurements = {}
9394
self.normalize_rates = True
95+
self.rawData = False
9496
self.key = None
9597
self.sensor = None
9698

@@ -188,26 +190,34 @@ def addMeasurement(self, meassure):
188190
def __str__(self):
189191
# dd = '-a' if self.includeDiskData else ''
190192
# Workaround for RTC Defect 280368: Zimon capacity query does not return all results (seen on CNSA)
191-
if (self.metrics
192-
and any(str(metric) in self.DISK_CAP_METRICS for metric in self.metrics)
193-
) or (self.sensor
194-
and self.sensor in ("GPFSDiskCap", "GPFSPoolCap", "GPFSInodeCap")
195-
):
196-
dd = '-ar'
197-
elif self.includeDiskData:
193+
dd = ''
194+
if self.includeDiskData:
198195
dd = '-a'
196+
if self.metrics:
197+
for metric in self.metrics:
198+
if any(map(metric.__contains__, self.DISK_CAP_METRICS)):
199+
dd = '-ar'
200+
break
201+
elif (self.sensor and self.sensor in ("GPFSDiskCap",
202+
"GPFSPoolCap",
203+
"GPFSInodeCap")
204+
):
205+
dd = '-ar'
206+
207+
if self.rawData:
208+
raw = '-z'
199209
else:
200-
dd = ''
210+
raw = ''
201211

202212
if self.sensor is not None:
203-
queryString = 'get -j {0} group {1} bucket_size {2} {3}'.format(
204-
dd, self.sensor, self.bucket_size, self.timeRep)
213+
queryString = 'get -j {0} {1} group {2} bucket_size {3} {4}'.format(
214+
dd, raw, self.sensor, self.bucket_size, self.timeRep)
205215
elif self.key is not None:
206-
queryString = 'get -j {0} {1} bucket_size {2} {3}'.format(
207-
dd, self.key, self.bucket_size, self.timeRep)
216+
queryString = 'get -j {0} {1} {2} bucket_size {3} {4}'.format(
217+
dd, raw, self.key, self.bucket_size, self.timeRep)
208218
else:
209-
queryString = 'get -j {0} metrics {1} bucket_size {2} {3}'.format(
210-
dd, ','.join(self.metrics), self.bucket_size, self.timeRep)
219+
queryString = 'get -j {0} {1} metrics {2} bucket_size {3} {4}'.format(
220+
dd, raw, ','.join(self.metrics), self.bucket_size, self.timeRep)
211221

212222
if self.filters:
213223
queryString += ' from ' + ",".join(self.filters)

source/queryHandler/QueryHandler.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,10 @@ def __init__(self, query, res_json):
164164
self.rows = self.__parseRows()
165165

166166
self.index_cache = {} # (metric, id) -> row value index
167-
self.ids = self._findIdentifiers()
167+
self.ids = None
168168

169169
if self.query and len(self.query.measurements) > 0:
170+
self.ids = self._findIdentifiers()
170171
self._populate_index_cache()
171172
self._add_calculated_colunm_headers()
172173

@@ -513,9 +514,12 @@ def __do_RESTCall(self, endpoint, requestType='GET', params=None):
513514

514515
try:
515516
_auth = getAuthHandler(*self.apiKeyData)
516-
_reqData = createRequestDataObj(self.logger, requestType, endpoint, self.server, self.port, auth=_auth, params=params)
517-
_request = perfHTTPrequestHelper(self.logger, reqdata=_reqData)
518-
_request.session.verify = self.caCert
517+
_reqData = createRequestDataObj(self.logger, requestType, endpoint,
518+
self.server, self.port, auth=_auth,
519+
params=params)
520+
_request = perfHTTPrequestHelper(self.logger,
521+
reqdata=_reqData,
522+
caCert=self.caCert)
519523
_response = _request.doRequest()
520524

521525
if _response.status_code == 200:

source/queryHandler/Topo.py

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
@author: HWASSMAN
2121
'''
2222

23-
24-
from collections import defaultdict
23+
import copy
24+
from collections import defaultdict, OrderedDict
2525
from typing import Set, List, Dict, DefaultDict
2626

2727
# dict.iteritems() deprecated in python 3
@@ -34,14 +34,14 @@ class Topo(object):
3434
'''
3535

3636
def __init__(self, jsonStr=None):
37-
self.topo = jsonStr
3837
self.__metricsDef = defaultdict(dict) # metrics dictionary, per sensor for all elements in the metadata
38+
self.__metricsType = defaultdict(dict) # metrics types dictionary
3939
self.__levels = defaultdict(dict) # component level priority dictionary, per sensor
4040
self.__ids = {} # fieldIds dictionary
4141
self.__groupKeys = {}
4242
self.__compTree = {}
43-
if self.topo:
44-
self._processMetadata(self.topo)
43+
if jsonStr:
44+
self._processMetadata(jsonStr)
4545

4646
def _processMetadata(self, metadata):
4747
'''
@@ -55,22 +55,24 @@ def _processMetadata(self, metadata):
5555
_components = defaultdict(set)
5656
# filters dictionary, per sensor, per (cluster or cluster_node) component
5757
_filters = defaultdict(list)
58+
# ordered dictionary to store filedLabel:fieldName pairs in order as they occur in metaStr
59+
_tags = OrderedDict()
5860
# name of the (cluster or cluster_node) component
5961
label = metaStr.get('fieldLabel')
6062

6163
if label in self.__compTree.keys():
6264
_components = self.__compTree[label]['componentsMap']
6365
_filters = self.__compTree[label]['filtersMap']
6466

65-
self._parse_topoJSONStr(self.__metricsDef, self.__levels, self.__ids, self.__groupKeys, _components, _filters, metaStr)
67+
self._parse_topoJSONStr(self.__metricsDef, self.__metricsType, self.__levels, self.__ids, self.__groupKeys, _components, _filters, _tags, metaStr)
6668
tree_entry = {}
6769
tree_entry['componentsMap'] = _components
6870
tree_entry['filtersMap'] = _filters
6971

7072
# comp_tree[label] = tree_entry
7173
self.__compTree[label] = tree_entry
7274

73-
def _parse_topoJSONStr(self, metrics, levels, ids, groupKeys, components, filters, metaStr):
75+
def _parse_topoJSONStr(self, metrics, metricsType, levels, ids, groupKeys, components, filters, _tags, metaStr):
7476
'''
7577
This function parses the 'node' or 'attribute' object found in the given JSON string (metaStr) in
7678
the componets or metrics dictionary. Also the used metric filters (per sensor) will be stored
@@ -80,15 +82,24 @@ def _parse_topoJSONStr(self, metrics, levels, ids, groupKeys, components, filter
8082

8183
field_value = metaStr['fieldLabel']
8284
field_name = metaStr['fieldName']
85+
field_type = metaStr['fieldSemantics']
8386

8487
# check if entity is a component
8588
if metaStr['type'] == 'node':
89+
if field_name == "sensor":
90+
# remove all partialKey tags from _tags dict except parent since we step in the new sensor level metaKey
91+
parent = _tags.popitem(last=False)
92+
_tags.clear()
93+
_tags.update([parent])
94+
else:
95+
_tags[field_name] = field_value
96+
8697
if field_value not in components[field_name]:
8798
components[field_name].add(field_value)
8899
# check if metaStr includes next level metaStr
89100
if 'keys' in metaStr and len(metaStr['keys']) > 0:
90101
for metaKey in metaStr['keys']:
91-
self._parse_topoJSONStr(metrics, levels, ids, groupKeys, components, filters, metaKey)
102+
self._parse_topoJSONStr(metrics, metricsType, levels, ids, groupKeys, components, filters, _tags, metaKey)
92103

93104
# check if entity is a metric
94105
elif metaStr['type'] == 'attribute':
@@ -100,21 +111,22 @@ def _parse_topoJSONStr(self, metrics, levels, ids, groupKeys, components, filter
100111

101112
if field_name not in iterval(metrics[sensor]):
102113
metrics[sensor][field_id] = field_name
114+
metricsType[field_name] = field_type
103115

104116
if groupKey not in groupKeys:
105117
# parse sensor relevant data f.e. groupKey, filters, levels
106118
groupKeys[groupKey] = len(groupKeys) + 1
107-
tags = {}
108-
levTree = {}
109-
for i, compValue in enumerate(partKey):
110-
for compLabel in components:
111-
if compValue in components[compLabel]:
112-
levTree[i + 1] = compLabel
113-
tags[compLabel] = compValue
114-
# if tags not in filters[sensor]:
115-
# not needed as groupkeys check will allow this to be reached only once
116-
filters[sensor].append(tags)
119+
group_tags = copy.deepcopy(_tags)
120+
121+
# if not all((value in tags.values()) for value in partKey):
122+
# rint("different key values")
123+
124+
filters[sensor].append(group_tags)
117125
if sensor not in levels:
126+
levTree = {}
127+
for i, tag_key in enumerate(group_tags.keys()):
128+
levTree[i + 1] = tag_key
129+
118130
levels[sensor] = levTree
119131

120132
# parse key id
@@ -177,6 +189,11 @@ def metricsSpec(self):
177189
''' Returns all defined metrics as dictionary of (metric_name : metric_id) items '''
178190
return self.__metricsDef
179191

192+
@property
193+
def metricsType(self):
194+
''' Returns a dictionary of (metric_name : metric_type) items '''
195+
return self.__metricsType
196+
180197
@property
181198
def getAllEnabledMetricsNames(self):
182199
''' Returns list of all found metrics names'''
@@ -201,7 +218,7 @@ def getSensorForMetric(self, searchMetric):
201218
if (searchMetric.find("(") >= 0):
202219
searchMetric = searchMetric[searchMetric.find("(") + 1:-1]
203220
for sensor, metrics in self.__metricsDef.items():
204-
if searchMetric in metrics.values():
221+
if searchMetric in set(metrics.values()):
205222
return sensor
206223
return None
207224

@@ -237,8 +254,11 @@ def getAllFilterMapsForSensor(self, searchSensor):
237254
based on metadata topology returned from zimon "topo".
238255
'''
239256
filtersMaps = []
240-
if searchSensor in self.allFiltersMaps.keys():
241-
filtersMaps.extend(self.allFiltersMaps[searchSensor])
257+
if searchSensor in set(self.sensorsSpec.keys()):
258+
for entryName in self.__compTree.keys():
259+
values = self.__compTree[entryName]['filtersMap'].get(searchSensor, [])
260+
if len(values) > 0:
261+
filtersMaps.extend(values)
242262
return filtersMaps
243263

244264
def getAllFilterMapsForMetric(self, searchMetric):
@@ -274,13 +294,11 @@ def getAllFilterKeysForMetric(self, searchMetric):
274294
return keys
275295

276296
def getAllFilterKeysForSensor(self, searchSensor):
277-
keys = []
297+
filter_keys = set()
278298
filtersMap = self.getAllFilterMapsForSensor(searchSensor)
279-
for a in filtersMap:
280-
keys.extend(list(a.keys()))
281-
if len(keys) > 1:
282-
return list(set(keys))
283-
return keys
299+
for filter in filtersMap:
300+
filter_keys.update(filter.keys())
301+
return list(filter_keys)
284302

285303
def getAllFilterKeysForMeasurementsMetrics(self, searchMetrics):
286304
filterKeys = []

0 commit comments

Comments
 (0)