event_processor.py 22 KB

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