host_performance.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from flask import Blueprint
  4. import json
  5. import jimit as ji
  6. import base64
  7. from jimvc.api.base import Base
  8. from jimvc.models import HostCPUMemory, HostTraffic, HostDiskUsageIO, Utils, Rules
  9. __author__ = 'James Iter'
  10. __date__ = '2017/8/7'
  11. __contact__ = 'james.iter.cn@gmail.com'
  12. __copyright__ = '(c) 2017 by James Iter.'
  13. blueprint = Blueprint(
  14. 'api_host_performance',
  15. __name__,
  16. url_prefix='/api/host_performance'
  17. )
  18. blueprints = Blueprint(
  19. 'api_host_performances',
  20. __name__,
  21. url_prefix='/api/host_performances'
  22. )
  23. host_cpu_memory = Base(the_class=HostCPUMemory, the_blueprint=blueprint, the_blueprints=blueprints)
  24. host_traffic = Base(the_class=HostTraffic, the_blueprint=blueprint, the_blueprints=blueprints)
  25. host_disk_usage_io = Base(the_class=HostDiskUsageIO, the_blueprint=blueprint, the_blueprints=blueprints)
  26. @Utils.dumps2response
  27. def r_cpu_memory_get_by_filter():
  28. return host_cpu_memory.get_by_filter()
  29. @Utils.dumps2response
  30. def r_traffic_get_by_filter():
  31. return host_traffic.get_by_filter()
  32. @Utils.dumps2response
  33. def r_disk_usage_io_get_by_filter():
  34. return host_disk_usage_io.get_by_filter()
  35. def get_performance_data(node_id, the_class=None, nic_name=None, mountpoint=None, granularity='hour'):
  36. args_rules = [
  37. Rules.NODE_ID.value,
  38. ]
  39. filters = list()
  40. try:
  41. ji.Check.previewing(args_rules, {'node_id': node_id})
  42. node_ids_str = 'node_id:in:' + node_id.__str__()
  43. filters.append(node_ids_str)
  44. if nic_name is not None:
  45. filters.append('name:eq:' + nic_name)
  46. if mountpoint is not None:
  47. filters.append('mountpoint:eq:' + mountpoint)
  48. ret = dict()
  49. ret['state'] = ji.Common.exchange_state(20000)
  50. ret['data'] = list()
  51. max_limit = 10080
  52. ts = ji.Common.ts()
  53. _boundary = ts - 60 * 60
  54. if granularity == 'hour':
  55. _boundary = ts - 60 * 60
  56. elif granularity == 'six_hours':
  57. _boundary = ts - 60 * 60 * 6
  58. elif granularity == 'day':
  59. _boundary = ts - 60 * 60 * 24
  60. elif granularity == 'seven_days':
  61. _boundary = ts - 60 * 60 * 24 * 7
  62. else:
  63. pass
  64. filters.append('timestamp:gt:' + _boundary.__str__())
  65. filter_str = ';'.join(filters)
  66. _rows, _rows_count = the_class.get_by_filter(
  67. offset=0, limit=max_limit, order_by='id', order='asc', filter_str=filter_str)
  68. def smooth_data(boundary=0, interval=60, now_ts=ji.Common.ts(), rows=None):
  69. needs = list()
  70. data = list()
  71. for t in range(boundary + interval, now_ts, interval):
  72. needs.append(t - t % interval)
  73. for row in rows:
  74. if row['timestamp'] % interval != 0:
  75. continue
  76. if needs.__len__() > 0:
  77. t = needs.pop(0)
  78. else:
  79. t = now_ts
  80. while t < row['timestamp']:
  81. data.append({
  82. 'timestamp': t,
  83. 'cpu_load': None,
  84. 'memory_available': None,
  85. 'rx_packets': None,
  86. 'rx_bytes': None,
  87. 'tx_packets': None,
  88. 'tx_bytes': None,
  89. 'rd_req': None,
  90. 'rd_bytes': None,
  91. 'used': None,
  92. 'wr_req': None,
  93. 'wr_bytes': None
  94. })
  95. if needs.__len__() > 0:
  96. t = needs.pop(0)
  97. else:
  98. t = now_ts
  99. data.append(row)
  100. return data
  101. if granularity == 'day':
  102. ret['data'] = smooth_data(boundary=_boundary, interval=600, now_ts=ts, rows=_rows)
  103. if granularity == 'seven_days':
  104. ret['data'] = smooth_data(boundary=_boundary, interval=600, now_ts=ts, rows=_rows)
  105. else:
  106. ret['data'] = smooth_data(boundary=_boundary, interval=60, now_ts=ts, rows=_rows)
  107. return ret
  108. except ji.PreviewingError, e:
  109. return json.loads(e.message)
  110. @Utils.dumps2response
  111. def r_cpu_memory_last_hour(node_id):
  112. return get_performance_data(node_id=node_id, the_class=HostCPUMemory, granularity='hour')
  113. @Utils.dumps2response
  114. def r_cpu_memory_last_six_hours(node_id):
  115. return get_performance_data(node_id=node_id, the_class=HostCPUMemory, granularity='six_hours')
  116. @Utils.dumps2response
  117. def r_cpu_memory_last_day(node_id):
  118. return get_performance_data(node_id=node_id, the_class=HostCPUMemory, granularity='day')
  119. @Utils.dumps2response
  120. def r_cpu_memory_last_seven_days(node_id):
  121. return get_performance_data(node_id=node_id, the_class=HostCPUMemory, granularity='seven_days')
  122. @Utils.dumps2response
  123. def r_traffic_last_hour(node_id, nic_name):
  124. return get_performance_data(node_id=node_id, nic_name=nic_name, the_class=HostTraffic, granularity='hour')
  125. @Utils.dumps2response
  126. def r_traffic_last_six_hours(node_id, nic_name):
  127. return get_performance_data(node_id=node_id, nic_name=nic_name, the_class=HostTraffic, granularity='six_hours')
  128. @Utils.dumps2response
  129. def r_traffic_last_day(node_id, nic_name):
  130. return get_performance_data(node_id=node_id, nic_name=nic_name, the_class=HostTraffic, granularity='day')
  131. @Utils.dumps2response
  132. def r_traffic_last_seven_days(node_id, nic_name):
  133. return get_performance_data(node_id=node_id, nic_name=nic_name, the_class=HostTraffic, granularity='seven_days')
  134. @Utils.dumps2response
  135. def r_disk_usage_io_last_hour(node_id, mountpoint):
  136. mountpoint = base64.b64decode(mountpoint)
  137. return get_performance_data(node_id=node_id, mountpoint=mountpoint, the_class=HostDiskUsageIO, granularity='hour')
  138. @Utils.dumps2response
  139. def r_disk_usage_io_last_six_hours(node_id, mountpoint):
  140. mountpoint = base64.b64decode(mountpoint)
  141. return get_performance_data(node_id=node_id, mountpoint=mountpoint, the_class=HostDiskUsageIO,
  142. granularity='six_hours')
  143. @Utils.dumps2response
  144. def r_disk_usage_io_last_day(node_id, mountpoint):
  145. mountpoint = base64.b64decode(mountpoint)
  146. return get_performance_data(node_id=node_id, mountpoint=mountpoint, the_class=HostDiskUsageIO, granularity='day')
  147. @Utils.dumps2response
  148. def r_disk_usage_io_last_seven_days(node_id, mountpoint):
  149. mountpoint = base64.b64decode(mountpoint)
  150. return get_performance_data(node_id=node_id, mountpoint=mountpoint, the_class=HostDiskUsageIO,
  151. granularity='seven_days')
  152. @Utils.dumps2response
  153. def r_current_top_10():
  154. # JimV 设计的 Hosts 容量为 100 个
  155. volume = 100
  156. limit = volume
  157. length = 10
  158. end_ts = ji.Common.ts() - 60
  159. start_ts = end_ts - 60
  160. # 避免落在时间边界上,导致过滤条件的范围落空
  161. if start_ts % 60 == 0:
  162. start_ts -= 1
  163. ret = dict()
  164. ret['state'] = ji.Common.exchange_state(20000)
  165. ret['data'] = {
  166. 'cpu_load': list(),
  167. 'rw_bytes': list(),
  168. 'rw_req': list(),
  169. 'rt_bytes': list(),
  170. 'rt_packets': list()
  171. }
  172. filter_str = ';'.join([':'.join(['timestamp', 'gt', start_ts.__str__()]),
  173. ':'.join(['timestamp', 'lt', end_ts.__str__()])])
  174. rows, _ = HostCPUMemory.get_by_filter(limit=limit, filter_str=filter_str)
  175. rows.sort(key=lambda k: k['cpu_load'], reverse=True)
  176. effective_range = length
  177. if rows.__len__() < length:
  178. effective_range = rows.__len__()
  179. for i in range(effective_range):
  180. if rows[i]['cpu_load'] == 0:
  181. break
  182. ret['data']['cpu_load'].append(rows[i])
  183. rows, _ = HostDiskUsageIO.get_by_filter(limit=limit, filter_str=filter_str)
  184. for i in range(rows.__len__()):
  185. rows[i]['rw_bytes'] = rows[i]['rd_bytes'] + rows[i]['wr_bytes']
  186. rows[i]['rw_req'] = rows[i]['rd_req'] + rows[i]['wr_req']
  187. effective_range = length
  188. if rows.__len__() < length:
  189. effective_range = rows.__len__()
  190. rows.sort(key=lambda k: k['rw_bytes'], reverse=True)
  191. for i in range(effective_range):
  192. if rows[i]['rw_req'] == 0:
  193. break
  194. ret['data']['rw_bytes'].append(rows[i])
  195. rows.sort(key=lambda k: k['rw_req'], reverse=True)
  196. for i in range(effective_range):
  197. if rows[i]['rw_req'] == 0:
  198. break
  199. ret['data']['rw_req'].append(rows[i])
  200. rows, _ = HostTraffic.get_by_filter(limit=limit, filter_str=filter_str)
  201. for i in range(rows.__len__()):
  202. rows[i]['rt_bytes'] = rows[i]['rx_bytes'] + rows[i]['tx_bytes']
  203. rows[i]['rt_packets'] = rows[i]['rx_packets'] + rows[i]['tx_packets']
  204. effective_range = length
  205. if rows.__len__() < length:
  206. effective_range = rows.__len__()
  207. rows.sort(key=lambda k: k['rt_bytes'], reverse=True)
  208. for i in range(effective_range):
  209. if rows[i]['rt_packets'] == 0:
  210. break
  211. ret['data']['rt_bytes'].append(rows[i])
  212. rows.sort(key=lambda k: k['rt_packets'], reverse=True)
  213. for i in range(effective_range):
  214. if rows[i]['rt_packets'] == 0:
  215. break
  216. ret['data']['rt_packets'].append(rows[i])
  217. return ret
  218. @Utils.dumps2response
  219. def r_last_the_range_minutes_top_10(_range):
  220. volume = 100
  221. limit = volume * _range
  222. length = 10
  223. end_ts = ji.Common.ts() - 60
  224. start_ts = end_ts - 60 * _range
  225. # 避免落在时间边界上,导致过滤条件的范围落空
  226. if start_ts % 60 == 0:
  227. start_ts -= 1
  228. ret = dict()
  229. ret['state'] = ji.Common.exchange_state(20000)
  230. ret['data'] = {
  231. 'cpu_load': list(),
  232. 'rw_bytes': list(),
  233. 'rw_req': list(),
  234. 'rt_bytes': list(),
  235. 'rt_packets': list()
  236. }
  237. filter_str = ';'.join([':'.join(['timestamp', 'gt', start_ts.__str__()]),
  238. ':'.join(['timestamp', 'lt', end_ts.__str__()])])
  239. # cpu 负载
  240. hosts_node_id_mapping = dict()
  241. rows, _ = HostCPUMemory.get_by_filter(limit=limit, filter_str=filter_str)
  242. for row in rows:
  243. if row['node_id'] not in hosts_node_id_mapping:
  244. hosts_node_id_mapping[row['node_id']] = {'cpu_load': 0, 'count': 0}
  245. hosts_node_id_mapping[row['node_id']]['cpu_load'] += row['cpu_load']
  246. hosts_node_id_mapping[row['node_id']]['count'] += 1.0
  247. rows = list()
  248. for k, v in hosts_node_id_mapping.items():
  249. # 忽略除数为 0 的情况
  250. if v['cpu_load'] == 0:
  251. continue
  252. rows.append({'node_id': k, 'cpu_load': v['cpu_load'] / v['count']})
  253. effective_range = length
  254. if rows.__len__() < length:
  255. effective_range = rows.__len__()
  256. rows.sort(key=lambda _k: _k['cpu_load'], reverse=True)
  257. ret['data']['cpu_load'] = rows[0:effective_range]
  258. # 磁盘使用统计
  259. hosts_node_id_mapping.clear()
  260. rows, _ = HostDiskUsageIO.get_by_filter(limit=limit, filter_str=filter_str)
  261. for row in rows:
  262. disk_uuid = '_'.join([row['node_id'].__str__(), row['mountpoint']])
  263. if disk_uuid not in hosts_node_id_mapping:
  264. hosts_node_id_mapping[disk_uuid] = {'rw_bytes': 0, 'rw_req': 0, 'node_id': row['node_id'],
  265. 'mountpoint': row['mountpoint']}
  266. hosts_node_id_mapping[disk_uuid]['rw_bytes'] += row['rd_bytes'] + row['wr_bytes']
  267. hosts_node_id_mapping[disk_uuid]['rw_req'] += row['rd_req'] + row['wr_req']
  268. rows = list()
  269. for k, v in hosts_node_id_mapping.items():
  270. # 过滤掉无操作的数据
  271. if v['rw_req'] == 0:
  272. continue
  273. rows.append({'disk_uuid': k, 'rw_bytes': v['rw_bytes'] * 60 * _range, 'rw_req': v['rw_req'] * 60 * _range,
  274. 'node_id': v['node_id'], 'mountpoint': v['mountpoint']})
  275. effective_range = length
  276. if rows.__len__() < length:
  277. effective_range = rows.__len__()
  278. rows.sort(key=lambda _k: _k['rw_bytes'], reverse=True)
  279. ret['data']['rw_bytes'] = rows[0:effective_range]
  280. rows.sort(key=lambda _k: _k['rw_req'], reverse=True)
  281. ret['data']['rw_req'] = rows[0:effective_range]
  282. # 网络流量
  283. hosts_node_id_mapping.clear()
  284. rows, _ = HostTraffic.get_by_filter(limit=limit, filter_str=filter_str)
  285. for row in rows:
  286. nic_uuid = '_'.join([row['node_id'].__str__(), row['name']])
  287. if nic_uuid not in hosts_node_id_mapping:
  288. hosts_node_id_mapping[nic_uuid] = {'rt_bytes': 0, 'rt_packets': 0, 'node_id': row['node_id'],
  289. 'name': row['name']}
  290. hosts_node_id_mapping[nic_uuid]['rt_bytes'] += row['rx_bytes'] + row['tx_bytes']
  291. hosts_node_id_mapping[nic_uuid]['rt_packets'] += row['rx_packets'] + row['tx_packets']
  292. rows = list()
  293. for k, v in hosts_node_id_mapping.items():
  294. # 过滤掉无流量的数据
  295. if v['rt_packets'] == 0:
  296. continue
  297. rows.append({'nic_uuid': k, 'rt_bytes': v['rt_bytes'] * 60 * _range,
  298. 'rt_packets': v['rt_packets'] * 60 * _range, 'node_id': v['node_id'], 'name': v['name']})
  299. effective_range = length
  300. if rows.__len__() < length:
  301. effective_range = rows.__len__()
  302. rows.sort(key=lambda _k: _k['rt_bytes'], reverse=True)
  303. ret['data']['rt_bytes'] = rows[0:effective_range]
  304. rows.sort(key=lambda _k: _k['rt_packets'], reverse=True)
  305. ret['data']['rt_packets'] = rows[0:effective_range]
  306. return ret
  307. @Utils.dumps2response
  308. def r_last_10_minutes_top_10():
  309. return r_last_the_range_minutes_top_10(_range=10)
  310. @Utils.dumps2response
  311. def r_last_hour_top_10():
  312. return r_last_the_range_minutes_top_10(_range=60)
  313. @Utils.dumps2response
  314. def r_last_six_hours_top_10():
  315. return r_last_the_range_minutes_top_10(_range=60 * 6)
  316. @Utils.dumps2response
  317. def r_last_day_top_10():
  318. return r_last_the_range_minutes_top_10(_range=60 * 24)