guest.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import copy
  4. from flask import Blueprint
  5. from flask import request
  6. import json
  7. from uuid import uuid4
  8. import jimit as ji
  9. from models import OSInitWrite
  10. from models.initialize import app, dev_table
  11. from models import Database as db
  12. from models import Config
  13. from models import GuestDisk
  14. from models import Rules
  15. from models import Utils
  16. from models import Guest
  17. from models import OSTemplate
  18. from models import GuestXML
  19. __author__ = 'James Iter'
  20. __date__ = '2017/3/22'
  21. __contact__ = 'james.iter.cn@gmail.com'
  22. __copyright__ = '(c) 2017 by James Iter.'
  23. blueprint = Blueprint(
  24. 'guest',
  25. __name__,
  26. url_prefix='/api/guest'
  27. )
  28. blueprints = Blueprint(
  29. 'guests',
  30. __name__,
  31. url_prefix='/api/guests'
  32. )
  33. @Utils.dumps2response
  34. def r_create():
  35. args_rules = [
  36. Rules.CPU.value,
  37. Rules.MEMORY.value,
  38. Rules.OS_TEMPLATE_ID.value,
  39. Rules.QUANTITY.value,
  40. Rules.NAME.value,
  41. Rules.PASSWORD.value,
  42. Rules.LEASE_TERM.value
  43. ]
  44. try:
  45. ret = dict()
  46. ret['state'] = ji.Common.exchange_state(20000)
  47. ji.Check.previewing(args_rules, request.json)
  48. config = Config()
  49. config.id = 1
  50. config.get()
  51. os_template = OSTemplate()
  52. os_template.id = request.json.get('os_template_id')
  53. if not os_template.exist():
  54. ret['state'] = ji.Common.exchange_state(40450)
  55. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template.id.__str__()])
  56. return ret
  57. os_template.get()
  58. os_init_writes, os_init_writes_count = OSInitWrite.get_by_filter(
  59. filter_str='os_init_id:in:' + os_template.os_init_id.__str__())
  60. if db.r.scard(app.config['ip_available_set']) < 1:
  61. ret['state'] = ji.Common.exchange_state(50350)
  62. return ret
  63. quantity = request.json.get('quantity')
  64. while quantity:
  65. quantity -= 1
  66. guest = Guest()
  67. guest.uuid = uuid4().__str__()
  68. guest.cpu = request.json.get('cpu')
  69. # 虚拟机内存单位,模板生成方法中已置其为GiB
  70. guest.memory = request.json.get('memory')
  71. guest.os_template_id = request.json.get('os_template_id')
  72. guest.name = request.json.get('name')
  73. guest.password = request.json.get('password')
  74. if guest.password is None or guest.password.__len__() < 1:
  75. guest.password = ji.Common.generate_random_code(length=16)
  76. while guest.name.__len__() < 1 or guest.exist_by('name'):
  77. guest.name = ji.Common.generate_random_code(length=8)
  78. guest.ip = db.r.spop(app.config['ip_available_set'])
  79. db.r.sadd(app.config['ip_used_set'], guest.ip)
  80. guest.network = config.vm_network
  81. guest.manage_network = config.vm_manage_network
  82. guest.vnc_port = db.r.spop(app.config['vnc_port_available_set'])
  83. db.r.sadd(app.config['vnc_port_used_set'], guest.vnc_port)
  84. guest.vnc_password = ji.Common.generate_random_code(length=16)
  85. guest_disk = {'uuid': uuid4().__str__(), 'size': -1, 'format': 'qcow2', 'sequence': 0}
  86. guest_xml = GuestXML(guest=guest, disk=guest_disk, config=config)
  87. guest.xml = guest_xml.get_domain()
  88. guest.create()
  89. # 替换占位符为有效内容
  90. _os_init_writes = copy.deepcopy(os_init_writes)
  91. for k, v in enumerate(_os_init_writes):
  92. _os_init_writes[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  93. replace('{HOSTNAME}', guest.name).\
  94. replace('{NETMASK}', config.netmask).\
  95. replace('{GATEWAY}', config.gateway).\
  96. replace('{DNS1}', config.dns1).\
  97. replace('{DNS2}', config.dns2)
  98. create_vm_msg = {
  99. 'action': 'create_vm',
  100. 'uuid': guest.uuid,
  101. 'name': guest.name,
  102. 'glusterfs_volume': config.glusterfs_volume,
  103. 'template_path': 'template_pool/' + os_template.name,
  104. 'guest_disk': guest_disk,
  105. 'writes': _os_init_writes,
  106. 'password': guest.password,
  107. 'xml': guest_xml.get_domain()
  108. }
  109. db.r.rpush(app.config['downstream_queue'], json.dumps(create_vm_msg, ensure_ascii=False))
  110. return ret
  111. except ji.PreviewingError, e:
  112. return json.loads(e.message)
  113. @Utils.dumps2response
  114. def r_reboot(uuids):
  115. args_rules = [
  116. Rules.UUIDS.value
  117. ]
  118. try:
  119. ji.Check.previewing(args_rules, {'uuids': uuids})
  120. guest = Guest()
  121. for uuid in uuids.split(','):
  122. guest.uuid = uuid
  123. guest.get_by('uuid')
  124. for uuid in uuids.split(','):
  125. message = {'action': 'reboot', 'uuid': uuid}
  126. Guest.emit_instruction(message=json.dumps(message))
  127. ret = dict()
  128. ret['state'] = ji.Common.exchange_state(20000)
  129. return ret
  130. except ji.PreviewingError, e:
  131. return json.loads(e.message)
  132. @Utils.dumps2response
  133. def r_force_reboot(uuids):
  134. args_rules = [
  135. Rules.UUIDS.value
  136. ]
  137. try:
  138. ji.Check.previewing(args_rules, {'uuids': uuids})
  139. guest = Guest()
  140. for uuid in uuids.split(','):
  141. guest.uuid = uuid
  142. guest.get_by('uuid')
  143. for uuid in uuids.split(','):
  144. message = {'action': 'force_reboot', 'uuid': uuid}
  145. Guest.emit_instruction(message=json.dumps(message))
  146. ret = dict()
  147. ret['state'] = ji.Common.exchange_state(20000)
  148. return ret
  149. except ji.PreviewingError, e:
  150. return json.loads(e.message)
  151. @Utils.dumps2response
  152. def r_shutdown(uuids):
  153. args_rules = [
  154. Rules.UUIDS.value
  155. ]
  156. try:
  157. ji.Check.previewing(args_rules, {'uuids': uuids})
  158. guest = Guest()
  159. for uuid in uuids.split(','):
  160. guest.uuid = uuid
  161. guest.get_by('uuid')
  162. for uuid in uuids.split(','):
  163. message = {'action': 'shutdown', 'uuid': uuid}
  164. Guest.emit_instruction(message=json.dumps(message))
  165. ret = dict()
  166. ret['state'] = ji.Common.exchange_state(20000)
  167. return ret
  168. except ji.PreviewingError, e:
  169. return json.loads(e.message)
  170. @Utils.dumps2response
  171. def r_force_shutdown(uuids):
  172. args_rules = [
  173. Rules.UUIDS.value
  174. ]
  175. try:
  176. ji.Check.previewing(args_rules, {'uuids': uuids})
  177. guest = Guest()
  178. for uuid in uuids.split(','):
  179. guest.uuid = uuid
  180. guest.get_by('uuid')
  181. for uuid in uuids.split(','):
  182. message = {'action': 'force_shutdown', 'uuid': uuid}
  183. Guest.emit_instruction(message=json.dumps(message))
  184. ret = dict()
  185. ret['state'] = ji.Common.exchange_state(20000)
  186. return ret
  187. except ji.PreviewingError, e:
  188. return json.loads(e.message)
  189. @Utils.dumps2response
  190. def r_boot(uuids):
  191. args_rules = [
  192. Rules.UUIDS.value
  193. ]
  194. try:
  195. ji.Check.previewing(args_rules, {'uuids': uuids})
  196. guest = Guest()
  197. for uuid in uuids.split(','):
  198. guest.uuid = uuid
  199. guest.get_by('uuid')
  200. for uuid in uuids.split(','):
  201. message = {'action': 'boot', 'uuid': uuid}
  202. Guest.emit_instruction(message=json.dumps(message))
  203. ret = dict()
  204. ret['state'] = ji.Common.exchange_state(20000)
  205. return ret
  206. except ji.PreviewingError, e:
  207. return json.loads(e.message)
  208. @Utils.dumps2response
  209. def r_suspend(uuids):
  210. args_rules = [
  211. Rules.UUIDS.value
  212. ]
  213. try:
  214. ji.Check.previewing(args_rules, {'uuids': uuids})
  215. guest = Guest()
  216. for uuid in uuids.split(','):
  217. guest.uuid = uuid
  218. guest.get_by('uuid')
  219. for uuid in uuids.split(','):
  220. message = {'action': 'suspend', 'uuid': uuid}
  221. Guest.emit_instruction(message=json.dumps(message))
  222. ret = dict()
  223. ret['state'] = ji.Common.exchange_state(20000)
  224. return ret
  225. except ji.PreviewingError, e:
  226. return json.loads(e.message)
  227. @Utils.dumps2response
  228. def r_resume(uuids):
  229. args_rules = [
  230. Rules.UUIDS.value
  231. ]
  232. try:
  233. ji.Check.previewing(args_rules, {'uuids': uuids})
  234. guest = Guest()
  235. for uuid in uuids.split(','):
  236. guest.uuid = uuid
  237. guest.get_by('uuid')
  238. for uuid in uuids.split(','):
  239. message = {'action': 'resume', 'uuid': uuid}
  240. Guest.emit_instruction(message=json.dumps(message))
  241. ret = dict()
  242. ret['state'] = ji.Common.exchange_state(20000)
  243. return ret
  244. except ji.PreviewingError, e:
  245. return json.loads(e.message)
  246. @Utils.dumps2response
  247. def r_delete(uuids):
  248. args_rules = [
  249. Rules.UUIDS.value
  250. ]
  251. # TODO: 加入是否删除使用的数据磁盘开关
  252. try:
  253. ji.Check.previewing(args_rules, {'uuids': uuids})
  254. guest = Guest()
  255. for uuid in uuids.split(','):
  256. guest.uuid = uuid
  257. guest.get_by('uuid')
  258. for uuid in uuids.split(','):
  259. message = {'action': 'delete', 'uuid': uuid}
  260. Guest.emit_instruction(message=json.dumps(message))
  261. ret = dict()
  262. ret['state'] = ji.Common.exchange_state(20000)
  263. return ret
  264. except ji.PreviewingError, e:
  265. return json.loads(e.message)
  266. @Utils.dumps2response
  267. def r_attach_disk(uuid, disk_uuid):
  268. args_rules = [
  269. Rules.UUID.value,
  270. Rules.DISK_UUID.value
  271. ]
  272. try:
  273. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  274. guest = Guest()
  275. guest.uuid = uuid
  276. guest.get_by('uuid')
  277. guest_disk = GuestDisk()
  278. guest_disk.uuid = disk_uuid
  279. guest_disk.get_by('uuid')
  280. ret = dict()
  281. ret['state'] = ji.Common.exchange_state(20000)
  282. # 判断欲挂载的磁盘是否空闲
  283. if guest_disk.guest_uuid.__len__() > 0:
  284. ret['state'] = ji.Common.exchange_state(41258)
  285. return ret
  286. # 取出该 guest 已挂载的磁盘,来做出决定,确定该磁盘的序列
  287. guest_disk.guest_uuid = guest.uuid
  288. disks, count = guest_disk.get_all()
  289. guest_disk.sequence = count + 1
  290. config = Config()
  291. config.id = 1
  292. config.get()
  293. guest_disk.update()
  294. xml = """
  295. <disk type='network' device='disk'>
  296. <driver name='qemu' type='qcow2' cache='none'/>
  297. <source protocol='gluster' name='{0}/DiskPool/{1}.{2}'>
  298. <host name='127.0.0.1' port='24007'/>
  299. </source>
  300. <target dev='{3}' bus='virtio'/>
  301. </disk>
  302. """.format(config.glusterfs_volume, guest_disk.uuid, guest_disk.format,
  303. dev_table[guest_disk.sequence])
  304. message = {'action': 'attach_disk', 'uuid': uuid, 'xml': xml}
  305. Guest.emit_instruction(message=json.dumps(message))
  306. return ret
  307. except ji.PreviewingError, e:
  308. return json.loads(e.message)
  309. @Utils.dumps2response
  310. def r_detach_disk(disk_uuid):
  311. args_rules = [
  312. Rules.DISK_UUID.value
  313. ]
  314. try:
  315. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  316. guest_disk = GuestDisk()
  317. guest_disk.uuid = disk_uuid
  318. guest_disk.get_by('uuid')
  319. ret = dict()
  320. ret['state'] = ji.Common.exchange_state(20000)
  321. if guest_disk.guest_uuid.__len__() != 36 and guest_disk.sequence < 1:
  322. # 表示未被任何实例使用,已被分离
  323. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  324. return ret
  325. guest_disk.guest_uuid = ''
  326. guest_disk.sequence = -1
  327. config = Config()
  328. config.id = 1
  329. config.get()
  330. guest_disk.update()
  331. xml = """
  332. <disk type='network' device='disk'>
  333. <driver name='qemu' type='qcow2' cache='none'/>
  334. <source protocol='gluster' name='{0}/DiskPool/{1}.{2}'>
  335. <host name='127.0.0.1' port='24007'/>
  336. </source>
  337. <target dev='{3}' bus='virtio'/>
  338. </disk>
  339. """.format(config.glusterfs_volume, guest_disk.uuid, guest_disk.format,
  340. dev_table[guest_disk.sequence])
  341. message = {'action': 'detach_disk', 'uuid': guest_disk.guest_uuid, 'xml': xml}
  342. Guest.emit_instruction(message=json.dumps(message))
  343. return ret
  344. except ji.PreviewingError, e:
  345. return json.loads(e.message)
  346. @Utils.dumps2response
  347. def r_migrate(uuids, destination_host):
  348. args_rules = [
  349. Rules.UUIDS.value,
  350. Rules.DESTINATION_HOST.value
  351. ]
  352. try:
  353. ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
  354. guest = Guest()
  355. for uuid in uuids.split(','):
  356. guest.uuid = uuid
  357. guest.get_by('uuid')
  358. for uuid in uuids.split(','):
  359. message = {'action': 'migrate', 'uuid': uuid, 'duri': 'qemu+ssh://' + destination_host + '/system'}
  360. Guest.emit_instruction(message=json.dumps(message))
  361. ret = dict()
  362. ret['state'] = ji.Common.exchange_state(20000)
  363. return ret
  364. except ji.PreviewingError, e:
  365. return json.loads(e.message)