guest.py 16 KB

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