event_processor.py 22 KB

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