hue_adapters.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # Licensed to Cloudera, Inc. under one
  2. # or more contributor license agreements. See the NOTICE file
  3. # distributed with this work for additional information
  4. # regarding copyright ownership. Cloudera, Inc. licenses this file
  5. # to you under the Apache License, Version 2.0 (the
  6. # "License"); you may not use this file except in compliance
  7. # with the License. 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. # Copyright (c) 2015 Cloudera, Inc. All rights reserved.
  17. import logging
  18. import time
  19. from cmf.monitor import schema
  20. from cmf.monitor.abstract_monitor import CollectionError
  21. from cmf.monitor.constants import WEB_METRICS_COLLECTION_GOOD, \
  22. DEFAULT_MONITOR_LOG_RATE, WEB_METRICS_COLLECTION_COMMUNICATION_FAILURE
  23. from cmf.monitor.generic import AbstractMetricCollector
  24. from cmf.monitor.generic.adapter import Adapter
  25. from cmf.monitor.generic.service_defined_metrics import ServiceDefinedMetrics
  26. from cmf.monitor.generic.utils import SplittingSourceProcessor, visit_json, \
  27. SimpleMetricsExtractor
  28. from cmf.throttling_logger import ThrottlingLogger
  29. from url_util import head_request_with_timeout
  30. logging.basicConfig()
  31. LOG = logging.getLogger('HueServerAdapter')
  32. THROTTLED_LOG = ThrottlingLogger(LOG, DEFAULT_MONITOR_LOG_RATE)
  33. class _HueIsAliveCollector(AbstractMetricCollector):
  34. """
  35. A collector that generates web-metric collection metrics from the hue is alive
  36. end-point.
  37. """
  38. def __init__(self, adapter):
  39. AbstractMetricCollector.__init__(self, adapter)
  40. if not isinstance(adapter, HueServerAdapter):
  41. raise Exception("Unsupported adapter type")
  42. def update_with_conf(self, conf):
  43. LOG.info("hue_adapters: _HueIsAliveCollector: update_with_conf: metrics_uri: %s" % self._adapter._get_is_alive_url(conf))
  44. self._metrics_uri = self._adapter._get_is_alive_url(conf)
  45. def collect_and_parse(self, conf):
  46. start = time.time()
  47. try:
  48. LOG.info("hue_adapters: _HueIsAliveCollector: _call_is_alive: metrics_uri: %s" % self._metrics_uri)
  49. self._call_is_alive(self._metrics_uri)
  50. result = WEB_METRICS_COLLECTION_GOOD
  51. except Exception, ex:
  52. THROTTLED_LOG.exception("Error calling is alive at '%s'" %
  53. (self._metrics_uri,))
  54. result = CollectionError(WEB_METRICS_COLLECTION_COMMUNICATION_FAILURE)
  55. now = time.time()
  56. self._metrics_tuple = result, now, int((now - start) * 1000)
  57. def _add_metrics(self, service_version, update, collected_metrics, duration):
  58. update.add_metric(schema.WEB_METRICS_COLLECTION_DURATION, duration)
  59. if isinstance(collected_metrics, CollectionError):
  60. update.add_metric(schema.WEB_METRICS_COLLECTION_STATUS,
  61. collected_metrics.code)
  62. else:
  63. update.add_metric(schema.WEB_METRICS_COLLECTION_STATUS,
  64. collected_metrics)
  65. return True
  66. def is_ready_to_report(self, conf, pid):
  67. if pid is None:
  68. raise Exception("Pid cannot be None")
  69. metrics_url = self._adapter._get_is_alive_url(conf)
  70. if metrics_url is None:
  71. return AbstractMetricCollector._COLLECTOR_NOT_SUPPORTED
  72. try:
  73. self._call_is_alive(metrics_url, 0.5)
  74. return True
  75. except Exception, ex:
  76. pass
  77. return False
  78. def _call_is_alive(self, is_alive_url, timeout=None):
  79. """
  80. We need this function to make testing eaiser.
  81. """
  82. head_request_with_timeout(is_alive_url, timeout=timeout)
  83. class HueServerAdapter(Adapter):
  84. """
  85. An adapter that collects and generates metrics for the HUE_SERVER. The adapter
  86. collects metrics from a sample file generated by hue. It also generates
  87. web-metric-collection metrics using an is-alive endpoint exposed by hue.
  88. Note that the web-metric-collection metrics generation is done as part of the
  89. sample file metric collection. The reason is that the hue is-alive end-point
  90. accepts HTTP HEAD requests and we can't use the regular web metric collector
  91. to generate these metics.
  92. """
  93. _HUE_SERVER_METRICS_SAMPLE_FILE_KEY = "location"
  94. _HUE_SERVER_HTTP_HOST_KEY = "http_host"
  95. _HUE_SERVER_HTTP_PORT_KEY = "http_port"
  96. _HUE_SERVER_SSL_ENABLED_KEY = "ssl_enable"
  97. _SERVICE_RELEASE = 'service_release'
  98. def __init__(self, safety_valve):
  99. Adapter.__init__(self, "HUE", "HUE_SERVER", safety_valve)
  100. self._metrics = None
  101. self._is_alive_collector = _HueIsAliveCollector(self)
  102. def read_service_defined_metrics(self, path):
  103. if path is None:
  104. raise Exception("A path is required!")
  105. self._metrics = ServiceDefinedMetrics(path, SplittingSourceProcessor('::'))
  106. def get_metrics_file(self, conf):
  107. if conf is None:
  108. raise Exception("A configuration is required!")
  109. try:
  110. return conf.get(
  111. self.section,
  112. HueServerAdapter._HUE_SERVER_METRICS_SAMPLE_FILE_KEY)
  113. except:
  114. LOG.exception("Failed to retrieve %s from monitoring configuration file" %
  115. HueServerAdapter._HUE_SERVER_METRICS_SAMPLE_FILE_KEY)
  116. def _get_is_alive_url(self, conf):
  117. if conf is None:
  118. raise Exception("A configuration is required!")
  119. if not self._is_alive_supported(conf):
  120. return None
  121. try:
  122. host = conf.get(self.section, HueServerAdapter._HUE_SERVER_HTTP_HOST_KEY)
  123. if host is None:
  124. LOG.error("%s entry missing from monitoring configuration file" %
  125. (HueServerAdapter._HUE_SERVER_HTTP_HOST_KEY, ))
  126. return None
  127. port = conf.getint(self.section,
  128. HueServerAdapter._HUE_SERVER_HTTP_PORT_KEY)
  129. if port is None:
  130. LOG.error("%s entry missing from monitoring configuration file" %
  131. (HueServerAdapter._HUE_SERVER_HTTP_PORT_KEY, ))
  132. return None
  133. if conf.getboolean_with_default(
  134. self.section,
  135. HueServerAdapter._HUE_SERVER_SSL_ENABLED_KEY,
  136. False):
  137. url_format = "https://%s:%s/desktop/debug/is_alive"
  138. else:
  139. url_format = "http://%s:%s/desktop/debug/is_alive"
  140. return url_format % (host, port)
  141. except:
  142. LOG.exception("Failed to read monitoring configuration file")
  143. return None
  144. def parse_metrics_from_file(self, conf, json):
  145. if json is None:
  146. raise Exception("a json sample is required")
  147. if self._metrics is None:
  148. raise Exception("No metrics have been loaded!")
  149. role_extractor = SimpleMetricsExtractor(
  150. self._metrics.get_sources(self._role_type))
  151. visit_json(json, [role_extractor])
  152. return role_extractor.metrics
  153. def add_sample_file_metrics(self, version, update, metrics, accessors):
  154. for metric_id, value in metrics.iteritems():
  155. update.add_metric(metric_id, value)
  156. def get_metrics_sample(self, version):
  157. return _HUE_SERVER_METRICS_SAMPLE
  158. def _is_alive_supported(self, conf):
  159. return True
  160. def get_adapter_specific_collectors(self):
  161. return [self._is_alive_collector]
  162. _HUE_SERVER_METRICS_SAMPLE="""
  163. {
  164. "desktop.auth.oauth.authentication-time": {
  165. "1m_rate": 0,
  166. "999_percentile": 0,
  167. "15m_rate": 0,
  168. "99_percentile": 1234567,
  169. "mean_rate": 0,
  170. "max": 0,
  171. "sum": 0,
  172. "min": 0,
  173. "5m_rate": 0,
  174. "count": 0,
  175. "75_percentile": 0,
  176. "std_dev": 0,
  177. "95_percentile": 0,
  178. "avg": 0
  179. },
  180. "desktop.auth.saml2.authentication-time": {
  181. "1m_rate": 0,
  182. "999_percentile": 0,
  183. "15m_rate": 0,
  184. "99_percentile": 0,
  185. "mean_rate": 0,
  186. "max": 0,
  187. "sum": 0,
  188. "min": 0,
  189. "5m_rate": 0,
  190. "count": 0,
  191. "75_percentile": 0,
  192. "std_dev": 0,
  193. "95_percentile": 0,
  194. "avg": 0
  195. },
  196. "python.threads.count": {
  197. "value": 42.5
  198. },
  199. "desktop.users.logged-in.count": {
  200. "count": 1
  201. },
  202. "python.gc.referrers.count": {
  203. "value": 0
  204. },
  205. "desktop.requests.exceptions.count": {
  206. "count": 7
  207. },
  208. "python.multiprocessing.active": {
  209. "value": 0
  210. },
  211. "desktop.users.count": {
  212. "value": 2
  213. },
  214. "python.gc.referents.count": {
  215. "value": 0
  216. },
  217. "python.threads.active": {
  218. "value": 52
  219. },
  220. "python.threads.daemon": {
  221. "value": 1
  222. },
  223. "desktop.auth.pam.authentication-time": {
  224. "1m_rate": 0,
  225. "999_percentile": 0,
  226. "15m_rate": 0,
  227. "99_percentile": 0,
  228. "mean_rate": 0,
  229. "max": 0,
  230. "sum": 0,
  231. "min": 0,
  232. "5m_rate": 0,
  233. "count": 0,
  234. "75_percentile": 0,
  235. "std_dev": 0,
  236. "95_percentile": 0,
  237. "avg": 0
  238. },
  239. "desktop.auth.spnego.authentication-time": {
  240. "1m_rate": 0,
  241. "999_percentile": 0,
  242. "15m_rate": 0,
  243. "99_percentile": 0,
  244. "mean_rate": 0,
  245. "max": 0,
  246. "sum": 0,
  247. "min": 0,
  248. "5m_rate": 0,
  249. "count": 0,
  250. "75_percentile": 0,
  251. "std_dev": 0,
  252. "95_percentile": 0,
  253. "avg": 0
  254. },
  255. "desktop.auth.ldap.authentication-time": {
  256. "1m_rate": 0,
  257. "999_percentile": 0,
  258. "15m_rate": 0,
  259. "99_percentile": 0,
  260. "mean_rate": 0,
  261. "max": 0,
  262. "sum": 0,
  263. "min": 0,
  264. "5m_rate": 0,
  265. "count": 0,
  266. "75_percentile": 0,
  267. "std_dev": 0,
  268. "95_percentile": 0,
  269. "avg": 0
  270. },
  271. "desktop.requests.aggregate-response-time": {
  272. "1m_rate": 2.5683487079247715e-101,
  273. "999_percentile": 11.331326007843018,
  274. "15m_rate": 4.33344429167876e-8,
  275. "99_percentile": 11.331326007843018,
  276. "mean_rate": 0.0005057717284166405,
  277. "max": 11.331326007843018,
  278. "sum": 15.07522988319397,
  279. "min": 0.007016897201538086,
  280. "5m_rate": 2.0635466335636803e-21,
  281. "count": 7,
  282. "75_percentile": 2.34737491607666,
  283. "std_dev": 4.1352759732033935,
  284. "95_percentile": 11.331326007843018,
  285. "avg": 2.15360426902771
  286. },
  287. "python.gc.objects.count": {
  288. "value": 218408
  289. },
  290. "python.multiprocessing.count": {
  291. "value": 0
  292. },
  293. "desktop.auth.openid.authentication-time": {
  294. "1m_rate": 0,
  295. "999_percentile": 0,
  296. "15m_rate": 0,
  297. "99_percentile": 0,
  298. "mean_rate": 0,
  299. "max": 0,
  300. "sum": 0,
  301. "min": 0,
  302. "5m_rate": 0,
  303. "count": 0,
  304. "75_percentile": 0,
  305. "std_dev": 0,
  306. "95_percentile": 0,
  307. "avg": 0
  308. },
  309. "python.gc.collection.count2": {
  310. "value": 10
  311. },
  312. "python.gc.collection.count0": {
  313. "value": 10
  314. },
  315. "python.gc.collection.count1": {
  316. "value": 10
  317. },
  318. "python.multiprocessing.daemon": {
  319. "value": 0
  320. },
  321. "desktop.requests.active.count": {
  322. "count": 0
  323. }
  324. }
  325. """