|
| 1 | +''' |
| 2 | +############################################################################## |
| 3 | +# Copyright 2023 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 Oct 25, 2023 |
| 19 | +
|
| 20 | +@author: HWASSMAN |
| 21 | +''' |
| 22 | + |
| 23 | +import cherrypy |
| 24 | + |
| 25 | +from queryHandler.QueryHandler import QueryHandler2 as QueryHandler |
| 26 | +from queryHandler.Topo import Topo |
| 27 | +from queryHandler import SensorConfig |
| 28 | +from messages import MSG |
| 29 | +from metaclasses import Singleton |
| 30 | +from timeit import default_timer as timer |
| 31 | +from time import sleep |
| 32 | + |
| 33 | + |
| 34 | +local_cache = [] |
| 35 | + |
| 36 | + |
| 37 | +class MetadataHandler(metaclass=Singleton): |
| 38 | + |
| 39 | + def __init__(self, **kwargs): |
| 40 | + self.__qh = None |
| 41 | + self.__sensorsConf = None |
| 42 | + self.__metaData = None |
| 43 | + self.__metricsDesc = {} |
| 44 | + self.logger = kwargs['logger'] |
| 45 | + self.server = kwargs['server'] |
| 46 | + self.port = kwargs['port'] |
| 47 | + self.apiKeyName = kwargs['apiKeyName'] |
| 48 | + self.apiKeyValue = kwargs['apiKeyValue'] |
| 49 | + self.caCertPath = kwargs.get('caCertPath', False) |
| 50 | + self.includeDiskData = kwargs.get('includeDiskData', False) |
| 51 | + self.sleepTime = kwargs.get('sleepTime', 60) |
| 52 | + |
| 53 | + self.__initializeTables() |
| 54 | + self.__getSupportedMetrics() |
| 55 | + |
| 56 | + @property |
| 57 | + def qh(self): |
| 58 | + if not self.__qh: |
| 59 | + self.__qh = QueryHandler(self.server, self.port, self.logger, self.apiKeyName, self.apiKeyValue, self.caCertPath) |
| 60 | + return self.__qh |
| 61 | + |
| 62 | + @property |
| 63 | + def SensorsConfig(self): |
| 64 | + if not self.__sensorsConf or len(self.__sensorsConf) == 0: |
| 65 | + self.__sensorsConf = SensorConfig.readSensorsConfigFromMMSDRFS(self.logger) |
| 66 | + if not self.__sensorsConf: |
| 67 | + raise ValueError(MSG['NoSensorConfigData']) |
| 68 | + return self.__sensorsConf |
| 69 | + |
| 70 | + @property |
| 71 | + def metaData(self): |
| 72 | + return self.__metaData |
| 73 | + |
| 74 | + @property |
| 75 | + def metricsDesc(self): |
| 76 | + return self.__metricsDesc |
| 77 | + |
| 78 | + def getSensorPeriod(self, metric): |
| 79 | + bucketSize = 0 |
| 80 | + sensor = self.metaData.getSensorForMetric(metric) |
| 81 | + if not sensor: |
| 82 | + self.logger.error(MSG['MetricErr'].format(metric)) |
| 83 | + raise cherrypy.HTTPError(404, MSG['MetricErr'].format(metric)) |
| 84 | + elif sensor in ('GPFSPoolCap', 'GPFSInodeCap'): |
| 85 | + sensor = 'GPFSDiskCap' |
| 86 | + elif sensor in ('GPFSNSDFS', 'GPFSNSDPool'): |
| 87 | + sensor = 'GPFSNSDDisk' |
| 88 | + elif sensor == 'DomainStore': |
| 89 | + return 1 |
| 90 | + |
| 91 | + for sensorAttr in self.SensorsConfig: |
| 92 | + if sensorAttr['name'] == str('\"%s\"' % sensor): |
| 93 | + bucketSize = int(sensorAttr['period']) |
| 94 | + return bucketSize |
| 95 | + |
| 96 | + def __getSupportedMetrics(self): |
| 97 | + """retrieve all defined (enabled and disabled) metrics list by querying topo -m""" |
| 98 | + |
| 99 | + metricSpec = {} |
| 100 | + |
| 101 | + outp = self.qh.getAvailableMetrics() |
| 102 | + |
| 103 | + if not outp or outp == "" or outp.startswith("Error:"): |
| 104 | + self.logger.warning(MSG['NoData']) |
| 105 | + return |
| 106 | + |
| 107 | + for line in outp.split("\n"): |
| 108 | + if len(line) > 0: |
| 109 | + tokens = line.split(";") |
| 110 | + if tokens and len(tokens) > 2: |
| 111 | + name = tokens[0] |
| 112 | + desc = tokens[2] or "No description provided" |
| 113 | + metricSpec[name] = desc |
| 114 | + else: |
| 115 | + self.logger.warning(MSG['DataWrongFormat'].format(line)) |
| 116 | + self.__metricsDesc = metricSpec |
| 117 | + |
| 118 | + def __initializeTables(self): |
| 119 | + '''Read the topology from ZIMon and (re-)construct |
| 120 | + the tables for metrics, keys, key elements (tag keys) |
| 121 | + and key values (tag values)''' |
| 122 | + |
| 123 | + self.__qh = QueryHandler(self.server, self.port, self.logger, self.apiKeyName, self.apiKeyValue) |
| 124 | + self.__sensorsConf = SensorConfig.readSensorsConfigFromMMSDRFS(self.logger) |
| 125 | + if not self.__sensorsConf: |
| 126 | + raise ValueError(MSG['NoSensorConfigData']) |
| 127 | + MAX_ATTEMPTS_COUNT = 3 |
| 128 | + for attempt in range(1, MAX_ATTEMPTS_COUNT + 1): |
| 129 | + self.__metaData = Topo(self.qh.getTopology()) |
| 130 | + if not (self.metaData and self.metaData.topo): |
| 131 | + if attempt > MAX_ATTEMPTS_COUNT: |
| 132 | + break |
| 133 | + # if no data returned because of the REST HTTP server is still starting, sleep and retry (max 3 times) |
| 134 | + self.logger.warning(MSG['NoDataStartNextAttempt'].format(attempt, MAX_ATTEMPTS_COUNT)) |
| 135 | + sleep(self.sleepTime) |
| 136 | + else: |
| 137 | + foundItems = len(self.metaData.allParents) - 1 |
| 138 | + sensors = self.metaData.sensorsSpec.keys() |
| 139 | + self.logger.info(MSG['MetaSuccess']) |
| 140 | + self.logger.details(MSG['ReceivAttrValues'].format('parents totally', foundItems)) |
| 141 | + self.logger.debug(MSG['ReceivAttrValues'].format('parents', ", ".join(self.metaData.allParents))) |
| 142 | + self.logger.info(MSG['ReceivAttrValues'].format('sensors', ", ".join(sensors))) |
| 143 | + return |
| 144 | + raise ValueError(MSG['NoData']) |
| 145 | + |
| 146 | + def update(self, refresh_all=False): |
| 147 | + '''Read the topology from ZIMon and update |
| 148 | + the tables for metrics, keys, key elements (tag keys) |
| 149 | + and key values (tag values)''' |
| 150 | + |
| 151 | + if refresh_all: |
| 152 | + self.__sensorsConf = SensorConfig.readSensorsConfigFromMMSDRFS(self.logger) |
| 153 | + |
| 154 | + tstart = timer() |
| 155 | + self.__metaData = Topo(self.qh.getTopology()) |
| 156 | + tend = timer() |
| 157 | + if not (self.metaData and self.metaData.topo): |
| 158 | + self.logger.error(MSG['NoData']) # Please check the pmcollector is properly configured and running. |
| 159 | + raise cherrypy.HTTPError(404, MSG[404]) |
| 160 | + self.logger.details(MSG['MetaSuccess']) |
| 161 | + self.logger.debug(MSG['ReceivAttrValues'].format('parents', ", ".join(self.metaData.allParents))) |
| 162 | + self.logger.debug(MSG['TimerInfo'].format('Metadata', str(tend - tstart))) |
| 163 | + return ({'msg': MSG['MetaSuccess']}) |
0 commit comments