event_processor.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import traceback
  4. import json
  5. import time
  6. from IPy import IP
  7. import jimit as ji
  8. from models import Database as db, Config, GuestCPUMemory, GuestTraffic, GuestDiskIO, SSHKeyGuestMapping
  9. from models import Guest
  10. from models import Disk
  11. from models import Snapshot, SnapshotDiskMapping
  12. from models import Log
  13. from models import Utils
  14. from models import EmitKind
  15. from models import ResponseState, GuestState, DiskState
  16. from models.guest import GuestMigrateInfo
  17. from models.initialize import app, logger
  18. from models.status import GuestCollectionPerformanceDataKind, HostCollectionPerformanceDataKind
  19. from models import HostCPUMemory, HostTraffic, HostDiskUsageIO
  20. __author__ = 'James Iter'
  21. __date__ = '2017/4/15'
  22. __contact__ = 'james.iter.cn@gmail.com'
  23. __copyright__ = '(c) 2017 by James Iter.'
  24. class EventProcessor(object):
  25. message = None
  26. log = Log()
  27. guest = Guest()
  28. guest_migrate_info = GuestMigrateInfo()
  29. disk = Disk()
  30. snapshot = Snapshot()
  31. snapshot_disk_mapping = SnapshotDiskMapping()
  32. config = Config()
  33. config.id = 1
  34. guest_cpu_memory = GuestCPUMemory()
  35. guest_traffic = GuestTraffic()
  36. guest_disk_io = GuestDiskIO()
  37. host_cpu_memory = HostCPUMemory()
  38. host_traffic = HostTraffic()
  39. host_disk_usage_io = HostDiskUsageIO()
  40. @classmethod
  41. def log_processor(cls):
  42. cls.log.set(type=cls.message['type'], timestamp=cls.message['timestamp'], host=cls.message['host'],
  43. message=cls.message['message'],
  44. full_message='' if cls.message['message'].__len__() < 255 else cls.message['message'])
  45. cls.log.create()
  46. @classmethod
  47. def guest_event_processor(cls):
  48. cls.guest.uuid = cls.message['message']['uuid']
  49. cls.guest.get_by('uuid')
  50. cls.guest.node_id = cls.message['node_id']
  51. last_status = cls.guest.status
  52. cls.guest.status = cls.message['type']
  53. if cls.message['type'] == GuestState.update.value:
  54. # 更新事件不改变 Guest 的状态
  55. cls.guest.status = last_status
  56. cls.guest.xml = cls.message['message']['xml']
  57. elif cls.guest.status == GuestState.migrating.value:
  58. try:
  59. cls.guest_migrate_info.uuid = cls.guest.uuid
  60. cls.guest_migrate_info.get_by('uuid')
  61. cls.guest_migrate_info.type = cls.message['message']['migrating_info']['type']
  62. cls.guest_migrate_info.time_elapsed = cls.message['message']['migrating_info']['time_elapsed']
  63. cls.guest_migrate_info.time_remaining = cls.message['message']['migrating_info']['time_remaining']
  64. cls.guest_migrate_info.data_total = cls.message['message']['migrating_info']['data_total']
  65. cls.guest_migrate_info.data_processed = cls.message['message']['migrating_info']['data_processed']
  66. cls.guest_migrate_info.data_remaining = cls.message['message']['migrating_info']['data_remaining']
  67. cls.guest_migrate_info.mem_total = cls.message['message']['migrating_info']['mem_total']
  68. cls.guest_migrate_info.mem_processed = cls.message['message']['migrating_info']['mem_processed']
  69. cls.guest_migrate_info.mem_remaining = cls.message['message']['migrating_info']['mem_remaining']
  70. cls.guest_migrate_info.file_total = cls.message['message']['migrating_info']['file_total']
  71. cls.guest_migrate_info.file_processed = cls.message['message']['migrating_info']['file_processed']
  72. cls.guest_migrate_info.file_remaining = cls.message['message']['migrating_info']['file_remaining']
  73. cls.guest_migrate_info.update()
  74. except ji.PreviewingError as e:
  75. ret = json.loads(e.message)
  76. if ret['state']['code'] == '404':
  77. cls.guest_migrate_info.type = cls.message['message']['migrating_info']['type']
  78. cls.guest_migrate_info.time_elapsed = cls.message['message']['migrating_info']['time_elapsed']
  79. cls.guest_migrate_info.time_remaining = cls.message['message']['migrating_info']['time_remaining']
  80. cls.guest_migrate_info.data_total = cls.message['message']['migrating_info']['data_total']
  81. cls.guest_migrate_info.data_processed = cls.message['message']['migrating_info']['data_processed']
  82. cls.guest_migrate_info.data_remaining = cls.message['message']['migrating_info']['data_remaining']
  83. cls.guest_migrate_info.mem_total = cls.message['message']['migrating_info']['mem_total']
  84. cls.guest_migrate_info.mem_processed = cls.message['message']['migrating_info']['mem_processed']
  85. cls.guest_migrate_info.mem_remaining = cls.message['message']['migrating_info']['mem_remaining']
  86. cls.guest_migrate_info.file_total = cls.message['message']['migrating_info']['file_total']
  87. cls.guest_migrate_info.file_processed = cls.message['message']['migrating_info']['file_processed']
  88. cls.guest_migrate_info.file_remaining = cls.message['message']['migrating_info']['file_remaining']
  89. cls.guest_migrate_info.create()
  90. elif cls.guest.status == GuestState.creating.value:
  91. if cls.message['message']['progress'] <= cls.guest.progress:
  92. return
  93. cls.guest.progress = cls.message['message']['progress']
  94. cls.guest.update()
  95. # 限定特殊情况下更新磁盘所属 Guest,避免迁移、创建时频繁被无意义的更新
  96. if cls.guest.status in [GuestState.running.value, GuestState.shutoff.value]:
  97. cls.disk.update_by_filter({'node_id': cls.guest.node_id}, filter_str='guest_uuid:eq:' + cls.guest.uuid)
  98. @classmethod
  99. def host_event_processor(cls):
  100. key = cls.message['message']['node_id']
  101. value = {
  102. 'hostname': cls.message['host'],
  103. 'cpu': cls.message['message']['cpu'],
  104. 'system_load': cls.message['message']['system_load'],
  105. 'memory': cls.message['message']['memory'],
  106. 'memory_available': cls.message['message']['memory_available'],
  107. 'interfaces': cls.message['message']['interfaces'],
  108. 'disks': cls.message['message']['disks'],
  109. 'boot_time': cls.message['message']['boot_time'],
  110. 'nonrandom': False,
  111. 'threads_status': cls.message['message']['threads_status'],
  112. 'timestamp': ji.Common.ts()
  113. }
  114. db.r.hset(app.config['hosts_info'], key=key, value=json.dumps(value, ensure_ascii=False))
  115. @classmethod
  116. def response_processor(cls):
  117. _object = cls.message['message']['_object']
  118. action = cls.message['message']['action']
  119. uuid = cls.message['message']['uuid']
  120. state = cls.message['type']
  121. data = cls.message['message']['data']
  122. node_id = cls.message['node_id']
  123. if _object == 'guest':
  124. if action == 'create':
  125. if state == ResponseState.success.value:
  126. # 系统盘的 UUID 与其 Guest 的 UUID 相同
  127. cls.disk.uuid = uuid
  128. cls.disk.get_by('uuid')
  129. cls.disk.guest_uuid = uuid
  130. cls.disk.state = DiskState.mounted.value
  131. # disk_info['virtual-size'] 的单位为Byte,需要除以 1024 的 3 次方,换算成单位为 GB 的值
  132. cls.disk.size = data['disk_info']['virtual-size'] / (1024 ** 3)
  133. cls.disk.update()
  134. else:
  135. cls.guest.uuid = uuid
  136. cls.guest.get_by('uuid')
  137. cls.guest.status = GuestState.dirty.value
  138. cls.guest.update()
  139. elif action == 'migrate':
  140. pass
  141. elif action == 'delete':
  142. if state == ResponseState.success.value:
  143. cls.config.get()
  144. cls.guest.uuid = uuid
  145. cls.guest.get_by('uuid')
  146. if IP(cls.config.start_ip).int() <= IP(cls.guest.ip).int() <= IP(cls.config.end_ip).int():
  147. if db.r.srem(app.config['ip_used_set'], cls.guest.ip):
  148. db.r.sadd(app.config['ip_available_set'], cls.guest.ip)
  149. if (cls.guest.vnc_port - cls.config.start_vnc_port) <= \
  150. (IP(cls.config.end_ip).int() - IP(cls.config.start_ip).int()):
  151. if db.r.srem(app.config['vnc_port_used_set'], cls.guest.vnc_port):
  152. db.r.sadd(app.config['vnc_port_available_set'], cls.guest.vnc_port)
  153. cls.guest.delete()
  154. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  155. cls.disk.uuid = uuid
  156. cls.disk.get_by('uuid')
  157. cls.disk.delete()
  158. cls.disk.update_by_filter({'guest_uuid': '', 'sequence': -1, 'state': DiskState.idle.value},
  159. filter_str='guest_uuid:eq:' + cls.guest.uuid)
  160. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', cls.guest.uuid]))
  161. elif action == 'reset_password':
  162. if state == ResponseState.success.value:
  163. cls.guest.uuid = uuid
  164. cls.guest.get_by('uuid')
  165. cls.guest.password = cls.message['message']['passback_parameters']['password']
  166. cls.guest.update()
  167. elif action == 'attach_disk':
  168. cls.disk.uuid = cls.message['message']['passback_parameters']['disk_uuid']
  169. cls.disk.get_by('uuid')
  170. if state == ResponseState.success.value:
  171. cls.disk.guest_uuid = uuid
  172. cls.disk.sequence = cls.message['message']['passback_parameters']['sequence']
  173. cls.disk.state = DiskState.mounted.value
  174. cls.disk.update()
  175. elif action == 'detach_disk':
  176. cls.disk.uuid = cls.message['message']['passback_parameters']['disk_uuid']
  177. cls.disk.get_by('uuid')
  178. if state == ResponseState.success.value:
  179. cls.disk.guest_uuid = ''
  180. cls.disk.sequence = -1
  181. cls.disk.state = DiskState.idle.value
  182. cls.disk.update()
  183. elif action == 'boot':
  184. if state == ResponseState.success.value:
  185. pass
  186. elif _object == 'disk':
  187. if action == 'create':
  188. cls.disk.uuid = uuid
  189. cls.disk.get_by('uuid')
  190. cls.disk.node_id = node_id
  191. if state == ResponseState.success.value:
  192. cls.disk.state = DiskState.idle.value
  193. else:
  194. cls.disk.state = DiskState.dirty.value
  195. cls.disk.update()
  196. elif action == 'resize':
  197. if state == ResponseState.success.value:
  198. cls.config.get()
  199. cls.disk.uuid = uuid
  200. cls.disk.get_by('uuid')
  201. cls.disk.size = cls.message['message']['passback_parameters']['size']
  202. cls.disk.quota(config=cls.config)
  203. cls.disk.update()
  204. elif action == 'delete':
  205. cls.disk.uuid = uuid
  206. cls.disk.get_by('uuid')
  207. cls.disk.delete()
  208. elif _object == 'snapshot':
  209. if action == 'create':
  210. if state == ResponseState.success.value:
  211. cls.snapshot.id = cls.message['message']['passback_parameters']['id']
  212. cls.snapshot.snapshot_id = data['snapshot_id']
  213. cls.snapshot.parent_id = data['parent_id']
  214. cls.snapshot.xml = data['xml']
  215. cls.snapshot.progress = 100
  216. cls.snapshot.update()
  217. disks, _ = Disk.get_by_filter(filter_str='guest_uuid:eq:' + cls.snapshot.guest_uuid)
  218. for disk in disks:
  219. cls.snapshot_disk_mapping.snapshot_id = cls.snapshot.snapshot_id
  220. cls.snapshot_disk_mapping.disk_uuid = disk['uuid']
  221. cls.snapshot_disk_mapping.create()
  222. else:
  223. cls.snapshot.progress = 255
  224. cls.snapshot.update()
  225. if action == 'delete':
  226. if state == ResponseState.success.value:
  227. cls.snapshot.id = cls.message['message']['passback_parameters']['id']
  228. cls.snapshot.get()
  229. # 更新子快照的 parent_id 为,当前快照的 parent_id。因为当前快照已被删除。
  230. Snapshot.update_by_filter({'parent_id': cls.snapshot.parent_id},
  231. filter_str='parent_id:eq:' + cls.snapshot.snapshot_id)
  232. SnapshotDiskMapping.delete_by_filter(
  233. filter_str=':'.join(['snapshot_id', 'eq', cls.snapshot.snapshot_id]))
  234. cls.snapshot.delete()
  235. else:
  236. pass
  237. else:
  238. pass
  239. @classmethod
  240. def guest_collection_performance_processor(cls):
  241. data_kind = cls.message['type']
  242. timestamp = ji.Common.ts()
  243. timestamp -= (timestamp % 60)
  244. data = cls.message['message']['data']
  245. if data_kind == GuestCollectionPerformanceDataKind.cpu_memory.value:
  246. for item in data:
  247. cls.guest_cpu_memory.guest_uuid = item['guest_uuid']
  248. cls.guest_cpu_memory.cpu_load = item['cpu_load']
  249. cls.guest_cpu_memory.memory_available = item['memory_available']
  250. cls.guest_cpu_memory.memory_unused = item['memory_unused']
  251. cls.guest_cpu_memory.timestamp = timestamp
  252. cls.guest_cpu_memory.create()
  253. if data_kind == GuestCollectionPerformanceDataKind.traffic.value:
  254. for item in data:
  255. cls.guest_traffic.guest_uuid = item['guest_uuid']
  256. cls.guest_traffic.name = item['name']
  257. cls.guest_traffic.rx_bytes = item['rx_bytes']
  258. cls.guest_traffic.rx_packets = item['rx_packets']
  259. cls.guest_traffic.rx_errs = item['rx_errs']
  260. cls.guest_traffic.rx_drop = item['rx_drop']
  261. cls.guest_traffic.tx_bytes = item['tx_bytes']
  262. cls.guest_traffic.tx_packets = item['tx_packets']
  263. cls.guest_traffic.tx_errs = item['tx_errs']
  264. cls.guest_traffic.tx_drop = item['tx_drop']
  265. cls.guest_traffic.timestamp = timestamp
  266. cls.guest_traffic.create()
  267. if data_kind == GuestCollectionPerformanceDataKind.disk_io.value:
  268. for item in data:
  269. cls.guest_disk_io.disk_uuid = item['disk_uuid']
  270. cls.guest_disk_io.rd_req = item['rd_req']
  271. cls.guest_disk_io.rd_bytes = item['rd_bytes']
  272. cls.guest_disk_io.wr_req = item['wr_req']
  273. cls.guest_disk_io.wr_bytes = item['wr_bytes']
  274. cls.guest_disk_io.timestamp = timestamp
  275. cls.guest_disk_io.create()
  276. else:
  277. pass
  278. @classmethod
  279. def host_collection_performance_processor(cls):
  280. data_kind = cls.message['type']
  281. timestamp = ji.Common.ts()
  282. timestamp -= (timestamp % 60)
  283. data = cls.message['message']['data']
  284. if data_kind == HostCollectionPerformanceDataKind.cpu_memory.value:
  285. cls.host_cpu_memory.node_id = data['node_id']
  286. cls.host_cpu_memory.cpu_load = data['cpu_load']
  287. cls.host_cpu_memory.memory_available = data['memory_available']
  288. cls.host_cpu_memory.timestamp = timestamp
  289. cls.host_cpu_memory.create()
  290. if data_kind == HostCollectionPerformanceDataKind.traffic.value:
  291. for item in data:
  292. cls.host_traffic.node_id = item['node_id']
  293. cls.host_traffic.name = item['name']
  294. cls.host_traffic.rx_bytes = item['rx_bytes']
  295. cls.host_traffic.rx_packets = item['rx_packets']
  296. cls.host_traffic.rx_errs = item['rx_errs']
  297. cls.host_traffic.rx_drop = item['rx_drop']
  298. cls.host_traffic.tx_bytes = item['tx_bytes']
  299. cls.host_traffic.tx_packets = item['tx_packets']
  300. cls.host_traffic.tx_errs = item['tx_errs']
  301. cls.host_traffic.tx_drop = item['tx_drop']
  302. cls.host_traffic.timestamp = timestamp
  303. cls.host_traffic.create()
  304. if data_kind == HostCollectionPerformanceDataKind.disk_usage_io.value:
  305. for item in data:
  306. cls.host_disk_usage_io.node_id = item['node_id']
  307. cls.host_disk_usage_io.mountpoint = item['mountpoint']
  308. cls.host_disk_usage_io.used = item['used']
  309. cls.host_disk_usage_io.rd_req = item['rd_req']
  310. cls.host_disk_usage_io.rd_bytes = item['rd_bytes']
  311. cls.host_disk_usage_io.wr_req = item['wr_req']
  312. cls.host_disk_usage_io.wr_bytes = item['wr_bytes']
  313. cls.host_disk_usage_io.timestamp = timestamp
  314. cls.host_disk_usage_io.create()
  315. else:
  316. pass
  317. @classmethod
  318. def launch(cls):
  319. logger.info(msg='Thread EventProcessor is launched.')
  320. while True:
  321. if Utils.exit_flag:
  322. msg = 'Thread EventProcessor say bye-bye'
  323. print msg
  324. logger.info(msg=msg)
  325. return
  326. try:
  327. report = db.r.lpop(app.config['upstream_queue'])
  328. if report is None:
  329. time.sleep(1)
  330. continue
  331. cls.message = json.loads(report)
  332. if cls.message['kind'] == EmitKind.log.value:
  333. cls.log_processor()
  334. elif cls.message['kind'] == EmitKind.guest_event.value:
  335. cls.guest_event_processor()
  336. elif cls.message['kind'] == EmitKind.host_event.value:
  337. cls.host_event_processor()
  338. elif cls.message['kind'] == EmitKind.response.value:
  339. cls.response_processor()
  340. elif cls.message['kind'] == EmitKind.guest_collection_performance.value:
  341. cls.guest_collection_performance_processor()
  342. elif cls.message['kind'] == EmitKind.host_collection_performance.value:
  343. cls.host_collection_performance_processor()
  344. else:
  345. pass
  346. except Exception as e:
  347. logger.error(traceback.format_exc())
  348. time.sleep(1)