disk.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from flask import Blueprint, request
  4. import json
  5. from uuid import uuid4
  6. import jimit as ji
  7. from models import Guest, DiskState
  8. from models.initialize import dev_table
  9. from models import Config
  10. from models import Disk
  11. from models import Rules
  12. from models import Utils
  13. from models.status import StorageMode
  14. from base import Base
  15. __author__ = 'James Iter'
  16. __date__ = '2017/4/24'
  17. __contact__ = 'james.iter.cn@gmail.com'
  18. __copyright__ = '(c) 2017 by James Iter.'
  19. blueprint = Blueprint(
  20. 'api_disk',
  21. __name__,
  22. url_prefix='/api/disk'
  23. )
  24. blueprints = Blueprint(
  25. 'api_disks',
  26. __name__,
  27. url_prefix='/api/disks'
  28. )
  29. disk_base = Base(the_class=Disk, the_blueprint=blueprint, the_blueprints=blueprints)
  30. @Utils.dumps2response
  31. def r_create():
  32. args_rules = [
  33. Rules.DISK_SIZE.value,
  34. Rules.REMARK.value,
  35. Rules.DISK_ON_HOST.value,
  36. Rules.QUANTITY.value
  37. ]
  38. config = Config()
  39. config.id = 1
  40. config.get()
  41. if config.storage_mode in [StorageMode.shared_mount.value, StorageMode.ceph.value,
  42. StorageMode.glusterfs.value]:
  43. request.json['on_host'] = 'shared_storage'
  44. try:
  45. ji.Check.previewing(args_rules, request.json)
  46. ret = dict()
  47. ret['state'] = ji.Common.exchange_state(20000)
  48. size = request.json['size']
  49. quantity = request.json['quantity']
  50. on_host = request.json['on_host']
  51. if size < 1:
  52. ret['state'] = ji.Common.exchange_state(41255)
  53. return ret
  54. while quantity:
  55. quantity -= 1
  56. disk = Disk()
  57. disk.guest_uuid = ''
  58. disk.size = size
  59. disk.uuid = uuid4().__str__()
  60. disk.remark = request.json.get('remark', '')
  61. disk.on_host = on_host
  62. disk.sequence = -1
  63. disk.format = 'qcow2'
  64. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  65. disk.quota(config=config)
  66. message = {
  67. '_object': 'disk',
  68. 'action': 'create',
  69. 'uuid': disk.uuid,
  70. 'storage_mode': config.storage_mode,
  71. 'dfs_volume': config.dfs_volume,
  72. 'hostname': disk.on_host,
  73. 'image_path': disk.path,
  74. 'size': disk.size
  75. }
  76. if disk.on_host == 'shared_storage':
  77. available_hosts = Guest.get_available_hosts()
  78. if available_hosts.__len__() == 0:
  79. ret['state'] = ji.Common.exchange_state(50351)
  80. return ret
  81. # 在可用计算节点中平均分配任务
  82. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  83. message['hostname'] = chosen_host['hostname']
  84. Guest.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  85. disk.create()
  86. return ret
  87. except ji.PreviewingError, e:
  88. return json.loads(e.message)
  89. @Utils.dumps2response
  90. def r_resize(uuid, size):
  91. args_rules = [
  92. Rules.UUID.value,
  93. Rules.DISK_SIZE_STR.value
  94. ]
  95. try:
  96. ji.Check.previewing(args_rules, {'uuid': uuid, 'size': size})
  97. disk = Disk()
  98. disk.uuid = uuid
  99. disk.get_by('uuid')
  100. ret = dict()
  101. ret['state'] = ji.Common.exchange_state(20000)
  102. if disk.size >= int(size):
  103. ret['state'] = ji.Common.exchange_state(41257)
  104. return ret
  105. config = Config()
  106. config.id = 1
  107. config.get()
  108. disk.size = int(size)
  109. disk.quota(config=config)
  110. # 将在事件返回层(models/event_processor.py:224 附近),更新数据库中 disk 对象
  111. message = {
  112. '_object': 'disk',
  113. 'action': 'resize',
  114. 'uuid': disk.uuid,
  115. 'guest_uuid': disk.guest_uuid,
  116. 'storage_mode': config.storage_mode,
  117. 'size': disk.size,
  118. 'dfs_volume': config.dfs_volume,
  119. 'hostname': disk.on_host,
  120. 'image_path': disk.path,
  121. 'disks': [disk.__dict__],
  122. 'passback_parameters': {'size': disk.size}
  123. }
  124. if disk.on_host == 'shared_storage':
  125. message['hostname'] = Guest.get_lightest_host()['hostname']
  126. if disk.guest_uuid.__len__() == 36:
  127. message['device_node'] = dev_table[disk.sequence]
  128. Guest.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  129. return ret
  130. except ji.PreviewingError, e:
  131. return json.loads(e.message)
  132. @Utils.dumps2response
  133. def r_delete(uuids):
  134. args_rules = [
  135. Rules.UUIDS.value
  136. ]
  137. try:
  138. ji.Check.previewing(args_rules, {'uuids': uuids})
  139. ret = dict()
  140. ret['state'] = ji.Common.exchange_state(20000)
  141. disk = Disk()
  142. # 检测所指定的 UUDIs 磁盘都存在
  143. for uuid in uuids.split(','):
  144. disk.uuid = uuid
  145. disk.get_by('uuid')
  146. if disk.state != DiskState.idle.value:
  147. ret['state'] = ji.Common.exchange_state(41256)
  148. return ret
  149. config = Config()
  150. config.id = 1
  151. config.get()
  152. # 执行删除操作
  153. for uuid in uuids.split(','):
  154. disk.uuid = uuid
  155. disk.get_by('uuid')
  156. message = {
  157. '_object': 'disk',
  158. 'action': 'delete',
  159. 'uuid': disk.uuid,
  160. 'storage_mode': config.storage_mode,
  161. 'dfs_volume': config.dfs_volume,
  162. 'hostname': disk.on_host,
  163. 'image_path': disk.path
  164. }
  165. if disk.on_host == 'shared_storage':
  166. message['hostname'] = Guest.get_lightest_host()['hostname']
  167. Guest.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  168. return ret
  169. except ji.PreviewingError, e:
  170. return json.loads(e.message)
  171. def add_device(func):
  172. from functools import wraps
  173. @wraps(func)
  174. def _add_device(*args, **kwargs):
  175. ret = func(*args, **kwargs)
  176. if ret['data'].__len__() > 0:
  177. if isinstance(ret['data'], list):
  178. for i, item in enumerate(ret['data']):
  179. ret['data'][i][u'device'] = u'/dev/' + dev_table[item['sequence']]
  180. if item['sequence'] < 0:
  181. ret['data'][i][u'device'] = None
  182. elif isinstance(ret['data'], dict):
  183. ret['data'][u'device'] = u'/dev/' + dev_table[ret['data']['sequence']]
  184. if ret['data']['sequence'] < 0:
  185. ret['data'][u'device'] = None
  186. else:
  187. raise json.dumps(ret)
  188. return ret
  189. return _add_device
  190. @Utils.dumps2response
  191. @add_device
  192. def r_get(uuids):
  193. return disk_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  194. @Utils.dumps2response
  195. @add_device
  196. def r_get_by_filter():
  197. return disk_base.get_by_filter()
  198. @Utils.dumps2response
  199. @add_device
  200. def r_content_search():
  201. return disk_base.content_search()
  202. @Utils.dumps2response
  203. def r_update(uuids):
  204. ret = dict()
  205. ret['state'] = ji.Common.exchange_state(20000)
  206. ret['data'] = list()
  207. args_rules = [
  208. Rules.UUIDS.value
  209. ]
  210. if 'remark' in request.json:
  211. args_rules.append(
  212. Rules.REMARK.value
  213. )
  214. if 'iops' in request.json:
  215. args_rules.append(
  216. Rules.IOPS.value
  217. )
  218. if 'iops_rd' in request.json:
  219. args_rules.append(
  220. Rules.IOPS_RD.value
  221. )
  222. if 'iops_wr' in request.json:
  223. args_rules.append(
  224. Rules.IOPS_WR.value
  225. )
  226. if 'iops_max' in request.json:
  227. args_rules.append(
  228. Rules.IOPS_MAX.value
  229. )
  230. if 'iops_max_length' in request.json:
  231. args_rules.append(
  232. Rules.IOPS_MAX_LENGTH.value
  233. )
  234. if 'bps' in request.json:
  235. args_rules.append(
  236. Rules.BPS.value
  237. )
  238. if 'bps_rd' in request.json:
  239. args_rules.append(
  240. Rules.BPS_RD.value
  241. )
  242. if 'bps_wr' in request.json:
  243. args_rules.append(
  244. Rules.BPS_WR.value
  245. )
  246. if 'bps_max' in request.json:
  247. args_rules.append(
  248. Rules.BPS_MAX.value
  249. )
  250. if 'bps_max_length' in request.json:
  251. args_rules.append(
  252. Rules.BPS_MAX_LENGTH.value
  253. )
  254. if args_rules.__len__() < 2:
  255. return ret
  256. request.json['uuids'] = uuids
  257. need_update_quota = False
  258. need_update_quota_parameters = ['iops', 'iops_rd', 'iops_wr', 'iops_max', 'iops_max_length',
  259. 'bps', 'bps_rd', 'bps_wr', 'bps_max', 'bps_max_length']
  260. if filter(lambda p: p in request.json, need_update_quota_parameters).__len__() > 0:
  261. need_update_quota = True
  262. try:
  263. ji.Check.previewing(args_rules, request.json)
  264. disk = Disk()
  265. # 检测所指定的 UUDIs 磁盘都存在
  266. for uuid in uuids.split(','):
  267. disk.uuid = uuid
  268. disk.get_by('uuid')
  269. for uuid in uuids.split(','):
  270. disk.uuid = uuid
  271. disk.get_by('uuid')
  272. disk.remark = request.json.get('remark', disk.remark)
  273. disk.iops = request.json.get('iops', disk.iops)
  274. disk.iops_rd = request.json.get('iops_rd', disk.iops_rd)
  275. disk.iops_wr = request.json.get('iops_wr', disk.iops_wr)
  276. disk.iops_max = request.json.get('iops_max', disk.iops_max)
  277. disk.iops_max_length = request.json.get('iops_max_length', disk.iops_max_length)
  278. disk.bps = request.json.get('bps', disk.bps)
  279. disk.bps_rd = request.json.get('bps_rd', disk.bps_rd)
  280. disk.bps_wr = request.json.get('bps_wr', disk.bps_wr)
  281. disk.bps_max = request.json.get('bps_max', disk.bps_max)
  282. disk.bps_max_length = request.json.get('bps_max_length', disk.bps_max_length)
  283. disk.update()
  284. disk.get()
  285. if disk.sequence >= 0 and need_update_quota:
  286. message = {
  287. '_object': 'disk',
  288. 'action': 'quota',
  289. 'uuid': disk.uuid,
  290. 'guest_uuid': disk.guest_uuid,
  291. 'hostname': disk.on_host,
  292. 'disks': [disk.__dict__]
  293. }
  294. Guest.emit_instruction(message=json.dumps(message))
  295. ret['data'].append(disk.__dict__)
  296. return ret
  297. except ji.PreviewingError, e:
  298. return json.loads(e.message)
  299. @Utils.dumps2response
  300. def r_distribute_count():
  301. from models import Disk
  302. rows, count = Disk.get_all()
  303. ret = dict()
  304. ret['state'] = ji.Common.exchange_state(20000)
  305. ret['data'] = {
  306. 'kind': {'system': 0, 'data_mounted': 0, 'data_idle': 0},
  307. 'total_size': 0,
  308. 'disks': rows.__len__()
  309. }
  310. for disk in rows:
  311. if disk['sequence'] == 0:
  312. ret['data']['kind']['system'] += 1
  313. elif disk['sequence'] < 0:
  314. ret['data']['kind']['data_idle'] += 1
  315. else:
  316. ret['data']['kind']['data_mounted'] += 1
  317. ret['data']['total_size'] += disk['size']
  318. return ret