snapshot.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from math import ceil
  4. import requests
  5. from flask import Blueprint, url_for
  6. from flask import request
  7. import json
  8. import jimit as ji
  9. import os
  10. from jimvc.api.base import Base
  11. from jimvc.models import Guest, Config, Disk
  12. from jimvc.models import Snapshot, SnapshotDiskMapping
  13. from jimvc.models import OSTemplateImage
  14. from jimvc.models import Utils
  15. from jimvc.models import Rules
  16. from jimvc.models import OSTemplateImageKind
  17. __author__ = 'James Iter'
  18. __date__ = '2018/4/10'
  19. __contact__ = 'james.iter.cn@gmail.com'
  20. __copyright__ = '(c) 2018 by James Iter.'
  21. blueprint = Blueprint(
  22. 'api_snapshot',
  23. __name__,
  24. url_prefix='/api/snapshot'
  25. )
  26. blueprints = Blueprint(
  27. 'api_snapshots',
  28. __name__,
  29. url_prefix='/api/snapshots'
  30. )
  31. snapshot_base = Base(the_class=Snapshot, the_blueprint=blueprint, the_blueprints=blueprints)
  32. @Utils.dumps2response
  33. def r_create():
  34. args_rules = [
  35. Rules.GUEST_UUID.value
  36. ]
  37. if 'label' in request.json:
  38. args_rules.append(
  39. Rules.LABEL.value,
  40. )
  41. try:
  42. ret = dict()
  43. ret['state'] = ji.Common.exchange_state(20000)
  44. ji.Check.previewing(args_rules, request.json)
  45. snapshot = Snapshot()
  46. guest = Guest()
  47. guest.uuid = request.json.get('guest_uuid')
  48. guest.get_by('uuid')
  49. snapshot.label = request.json.get('label', '')
  50. snapshot.status = guest.status
  51. snapshot.guest_uuid = guest.uuid
  52. snapshot.snapshot_id = '_'.join(['tmp', ji.Common.generate_random_code(length=8)])
  53. snapshot.parent_id = '-'
  54. snapshot.progress = 0
  55. snapshot.create()
  56. snapshot.get_by('snapshot_id')
  57. message = {
  58. '_object': 'snapshot',
  59. 'action': 'create',
  60. 'uuid': guest.uuid,
  61. 'node_id': guest.node_id,
  62. 'passback_parameters': {'id': snapshot.id}
  63. }
  64. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  65. ret['data'] = snapshot.__dict__
  66. return ret
  67. except ji.PreviewingError, e:
  68. return json.loads(e.message)
  69. @Utils.dumps2response
  70. def r_update(snapshot_id):
  71. snapshot = Snapshot()
  72. args_rules = [
  73. Rules.SNAPSHOT_ID.value
  74. ]
  75. if 'label' in request.json:
  76. args_rules.append(
  77. Rules.LABEL.value,
  78. )
  79. if args_rules.__len__() < 2:
  80. ret = dict()
  81. ret['state'] = ji.Common.exchange_state(20000)
  82. return ret
  83. request.json['snapshot_id'] = snapshot_id
  84. try:
  85. ji.Check.previewing(args_rules, request.json)
  86. snapshot.snapshot_id = request.json.get('snapshot_id')
  87. snapshot.get_by('snapshot_id')
  88. snapshot.label = request.json.get('label', snapshot.label)
  89. snapshot.update()
  90. snapshot.get()
  91. ret = dict()
  92. ret['state'] = ji.Common.exchange_state(20000)
  93. ret['data'] = snapshot.__dict__
  94. return ret
  95. except ji.PreviewingError, e:
  96. return json.loads(e.message)
  97. @Utils.dumps2response
  98. def r_get(snapshots_id):
  99. return snapshot_base.get(ids=snapshots_id, ids_rule=Rules.SNAPSHOTS_ID.value, by_field='snapshot_id')
  100. @Utils.dumps2response
  101. def r_get_by_filter():
  102. return snapshot_base.get_by_filter()
  103. @Utils.dumps2response
  104. def r_content_search():
  105. return snapshot_base.content_search()
  106. @Utils.dumps2response
  107. def r_delete(snapshots_id):
  108. args_rules = [
  109. Rules.SNAPSHOTS_ID.value
  110. ]
  111. try:
  112. ji.Check.previewing(args_rules, {'snapshots_id': snapshots_id})
  113. snapshot = Snapshot()
  114. guest = Guest()
  115. # 检测所指定的 快照 都存在
  116. for snapshot_id in snapshots_id.split(','):
  117. snapshot.snapshot_id = snapshot_id
  118. snapshot.get_by('snapshot_id')
  119. guest.uuid = snapshot.guest_uuid
  120. guest.get_by('uuid')
  121. # 执行删除操作
  122. for snapshot_id in snapshots_id.split(','):
  123. snapshot.snapshot_id = snapshot_id
  124. snapshot.get_by('snapshot_id')
  125. guest.uuid = snapshot.guest_uuid
  126. guest.get_by('uuid')
  127. message = {
  128. '_object': 'snapshot',
  129. 'action': 'delete',
  130. 'uuid': snapshot.guest_uuid,
  131. 'snapshot_id': snapshot.snapshot_id,
  132. 'node_id': guest.node_id,
  133. 'passback_parameters': {'id': snapshot.id}
  134. }
  135. Utils.emit_instruction(message=json.dumps(message))
  136. # 删除创建失败的 快照
  137. if snapshot.progress == 255:
  138. SnapshotDiskMapping.delete_by_filter(filter_str=':'.join(['snapshot_id', 'eq', snapshot.snapshot_id]))
  139. snapshot.delete()
  140. else:
  141. snapshot.progress = 254
  142. snapshot.update()
  143. ret = dict()
  144. ret['state'] = ji.Common.exchange_state(20000)
  145. return ret
  146. except ji.PreviewingError, e:
  147. return json.loads(e.message)
  148. @Utils.dumps2response
  149. def r_revert(snapshot_id):
  150. args_rules = [
  151. Rules.SNAPSHOT_ID.value
  152. ]
  153. try:
  154. ret = dict()
  155. ret['state'] = ji.Common.exchange_state(20000)
  156. ji.Check.previewing(args_rules, {'snapshot_id': snapshot_id})
  157. snapshot = Snapshot()
  158. guest = Guest()
  159. snapshot.snapshot_id = snapshot_id
  160. snapshot.get_by('snapshot_id')
  161. snapshot.progress = 253
  162. snapshot.update()
  163. snapshot.get()
  164. guest.uuid = snapshot.guest_uuid
  165. guest.get_by('uuid')
  166. message = {
  167. '_object': 'snapshot',
  168. 'action': 'revert',
  169. 'uuid': guest.uuid,
  170. 'snapshot_id': snapshot.snapshot_id,
  171. 'node_id': guest.node_id,
  172. 'passback_parameters': {'id': snapshot.id}
  173. }
  174. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  175. ret['data'] = snapshot.__dict__
  176. return ret
  177. except ji.PreviewingError, e:
  178. return json.loads(e.message)
  179. @Utils.dumps2response
  180. def r_get_disks(snapshot_id):
  181. args_rules = [
  182. Rules.SNAPSHOT_ID.value
  183. ]
  184. try:
  185. ret = dict()
  186. ret['state'] = ji.Common.exchange_state(20000)
  187. ret['data'] = list()
  188. ji.Check.previewing(args_rules, {'snapshot_id': snapshot_id})
  189. rows, _ = SnapshotDiskMapping.get_by_filter(filter_str=':'.join(['snapshot_id', 'eq', snapshot_id]))
  190. for row in rows:
  191. ret['data'].append(row['disk_uuid'])
  192. return ret
  193. except ji.PreviewingError, e:
  194. return json.loads(e.message)
  195. @Utils.dumps2response
  196. def r_get_snapshots_by_disks_uuid(disks_uuid):
  197. args_rules = [
  198. Rules.UUIDS.value
  199. ]
  200. try:
  201. ret = dict()
  202. ret['state'] = ji.Common.exchange_state(20000)
  203. ret['data'] = list()
  204. ji.Check.previewing(args_rules, {'uuids': disks_uuid})
  205. rows, _ = SnapshotDiskMapping.get_by_filter(filter_str=':'.join(['disk_uuid', 'in', disks_uuid]))
  206. ret['data'] = rows
  207. return ret
  208. except ji.PreviewingError, e:
  209. return json.loads(e.message)
  210. @Utils.dumps2response
  211. def r_convert_to_os_template_image(snapshot_id, disk_uuid):
  212. args_rules = [
  213. Rules.SNAPSHOT_ID.value,
  214. Rules.DISK_UUID.value,
  215. Rules.LABEL.value
  216. ]
  217. try:
  218. ret = dict()
  219. ret['state'] = ji.Common.exchange_state(20000)
  220. ji.Check.previewing(args_rules, {'snapshot_id': snapshot_id, 'disk_uuid': disk_uuid,
  221. 'label': request.json.get('label')})
  222. rows, _ = SnapshotDiskMapping.get_by_filter(filter_str=':'.join(['snapshot_id', 'eq', snapshot_id]))
  223. disks_uuid = list()
  224. for row in rows:
  225. disks_uuid.append(row['disk_uuid'])
  226. if disk_uuid not in disks_uuid:
  227. ret['state'] = ji.Common.exchange_state(40401)
  228. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], u': 未在快照: ',
  229. snapshot_id, u' 中找到磁盘:', disk_uuid])
  230. return ret
  231. config = Config()
  232. config.id = 1
  233. config.get()
  234. snapshot = Snapshot()
  235. os_template_image = OSTemplateImage()
  236. guest = Guest()
  237. disk = Disk()
  238. snapshot.snapshot_id = snapshot_id
  239. snapshot.get_by('snapshot_id')
  240. snapshot.progress = 252
  241. guest.uuid = snapshot.guest_uuid
  242. guest.get_by('uuid')
  243. disk.uuid = disk_uuid
  244. disk.get_by('uuid')
  245. os_template_image.id = guest.os_template_image_id
  246. os_template_image.get()
  247. image_name = '_'.join([snapshot.snapshot_id, disk.uuid]) + '.' + disk.format
  248. os_template_image.id = 0
  249. os_template_image.label = request.json.get('label')
  250. os_template_image.path = '/'.join([os.path.dirname(os_template_image.path), image_name])
  251. os_template_image.kind = OSTemplateImageKind.custom.value
  252. os_template_image.progress = 0
  253. os_template_image.create_time = ji.Common.tus()
  254. if os_template_image.exist_by('path'):
  255. ret['state'] = ji.Common.exchange_state(40901)
  256. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.path])
  257. return ret
  258. os_template_image.create()
  259. os_template_image.get_by('path')
  260. message = {
  261. '_object': 'snapshot',
  262. 'action': 'convert',
  263. 'uuid': disk.guest_uuid,
  264. 'snapshot_id': snapshot.snapshot_id,
  265. 'storage_mode': config.storage_mode,
  266. 'dfs_volume': config.dfs_volume,
  267. 'node_id': disk.node_id,
  268. 'snapshot_path': disk.path,
  269. 'template_path': os_template_image.path,
  270. 'os_template_image_id': os_template_image.id,
  271. 'passback_parameters': {'id': snapshot.snapshot_id, 'os_template_image_id': os_template_image.id}
  272. }
  273. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  274. snapshot.update()
  275. return ret
  276. except ji.PreviewingError, e:
  277. return json.loads(e.message)
  278. @Utils.dumps2response
  279. def r_show():
  280. args = list()
  281. page = int(request.args.get('page', 1))
  282. page_size = int(request.args.get('page_size', 10))
  283. keyword = request.args.get('keyword', None)
  284. order_by = request.args.get('order_by', None)
  285. order = request.args.get('order', 'desc')
  286. if page is not None:
  287. args.append('page=' + page.__str__())
  288. if page_size is not None:
  289. args.append('page_size=' + page_size.__str__())
  290. if keyword is not None:
  291. args.append('keyword=' + keyword.__str__())
  292. if order_by is not None:
  293. args.append('order_by=' + order_by)
  294. if order is not None:
  295. args.append('order=' + order)
  296. snapshots_url = url_for('api_snapshots.r_get_by_filter', _external=True)
  297. if keyword is not None:
  298. snapshots_url = url_for('api_snapshots.r_content_search', _external=True)
  299. if args.__len__() > 0:
  300. snapshots_url = snapshots_url + '?' + '&'.join(args)
  301. snapshots_ret = requests.get(url=snapshots_url, cookies=request.cookies)
  302. snapshots_ret = json.loads(snapshots_ret.content)
  303. guests_uuid = list()
  304. for snapshot in snapshots_ret['data']:
  305. guests_uuid.append(snapshot['guest_uuid'])
  306. guests, _ = Guest.get_by_filter(filter_str='uuid:in:' + ','.join(guests_uuid))
  307. # Guest uuid 与 Guest 的映射
  308. guests_mapping_by_uuid = dict()
  309. for guest in guests:
  310. guests_mapping_by_uuid[guest['uuid']] = guest
  311. for i, snapshot in enumerate(snapshots_ret['data']):
  312. if snapshot['guest_uuid'].__len__() == 36:
  313. snapshots_ret['data'][i]['guest'] = guests_mapping_by_uuid[snapshot['guest_uuid']]
  314. last_page = int(ceil(1 / float(page_size)))
  315. page_length = 5
  316. pages = list()
  317. if page < int(ceil(page_length / 2.0)):
  318. for i in range(1, page_length + 1):
  319. pages.append(i)
  320. if i == last_page or last_page == 0:
  321. break
  322. elif last_page - page < page_length / 2:
  323. for i in range(last_page - page_length + 1, last_page + 1):
  324. if i < 1:
  325. continue
  326. pages.append(i)
  327. else:
  328. for i in range(page - page_length / 2, page + int(ceil(page_length / 2.0))):
  329. pages.append(i)
  330. if i == last_page or last_page == 0:
  331. break
  332. ret = dict()
  333. ret['state'] = ji.Common.exchange_state(20000)
  334. ret['data'] = {
  335. 'snapshots': snapshots_ret['data'],
  336. 'paging': snapshots_ret['paging'],
  337. 'guests_mapping_by_uuid': guests_mapping_by_uuid,
  338. 'page': page,
  339. 'page_size': page_size,
  340. 'keyword': keyword,
  341. 'pages': pages,
  342. 'last_page': last_page,
  343. 'order_by': order_by,
  344. 'order': order
  345. }
  346. return ret