guest.py 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import copy
  4. from flask import Blueprint, url_for
  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, Host
  11. from models.initialize import app, dev_table
  12. from models import Database as db
  13. from models import Config
  14. from models import Disk
  15. from models import Rules
  16. from models import Utils
  17. from models import Guest
  18. from models import OSTemplateImage
  19. from models import OSTemplateProfile
  20. from models import OSTemplateInitializeOperate
  21. from models import GuestXML
  22. from models import SSHKeyGuestMapping
  23. from models import SSHKey
  24. from models import Snapshot
  25. from models import status
  26. __author__ = 'James Iter'
  27. __date__ = '2017/3/22'
  28. __contact__ = 'james.iter.cn@gmail.com'
  29. __copyright__ = '(c) 2017 by James Iter.'
  30. blueprint = Blueprint(
  31. 'api_guest',
  32. __name__,
  33. url_prefix='/api/guest'
  34. )
  35. blueprints = Blueprint(
  36. 'api_guests',
  37. __name__,
  38. url_prefix='/api/guests'
  39. )
  40. guest_base = Base(the_class=Guest, the_blueprint=blueprint, the_blueprints=blueprints)
  41. @Utils.dumps2response
  42. def r_create():
  43. args_rules = [
  44. Rules.CPU.value,
  45. Rules.MEMORY.value,
  46. Rules.BANDWIDTH.value,
  47. Rules.BANDWIDTH_UNIT.value,
  48. Rules.OS_TEMPLATE_IMAGE_ID.value,
  49. Rules.QUANTITY.value,
  50. Rules.REMARK.value,
  51. Rules.PASSWORD.value,
  52. Rules.LEASE_TERM.value
  53. ]
  54. if 'node_id' in request.json:
  55. args_rules.append(
  56. Rules.NODE_ID.value
  57. )
  58. if 'ssh_keys_id' in request.json:
  59. args_rules.append(
  60. Rules.SSH_KEYS_ID.value
  61. )
  62. try:
  63. ret = dict()
  64. ret['state'] = ji.Common.exchange_state(20000)
  65. ji.Check.previewing(args_rules, request.json)
  66. config = Config()
  67. config.id = 1
  68. config.get()
  69. os_template_image = OSTemplateImage()
  70. os_template_profile = OSTemplateProfile()
  71. os_template_image.id = request.json.get('os_template_image_id')
  72. if not os_template_image.exist():
  73. ret['state'] = ji.Common.exchange_state(40450)
  74. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.id.__str__()])
  75. return ret
  76. os_template_image.get()
  77. os_template_profile.id = os_template_image.os_template_profile_id
  78. os_template_profile.get()
  79. os_template_initialize_operates, os_template_initialize_operates_count = \
  80. OSTemplateInitializeOperate.get_by_filter(
  81. filter_str='os_template_initialize_operate_set_id:eq:' +
  82. os_template_profile.os_template_initialize_operate_set_id.__str__())
  83. if db.r.scard(app.config['ip_available_set']) < 1:
  84. ret['state'] = ji.Common.exchange_state(50350)
  85. return ret
  86. node_id = request.json.get('node_id', None)
  87. # 默认只取可随机分配虚拟机的 hosts
  88. available_hosts = Host.get_available_hosts(nonrandom=False)
  89. # 当指定了 host 时,取全部活着的 hosts
  90. if node_id is not None:
  91. available_hosts = Host.get_available_hosts(nonrandom=None)
  92. if available_hosts.__len__() == 0:
  93. ret['state'] = ji.Common.exchange_state(50351)
  94. return ret
  95. if node_id is not None and node_id not in [host['node_id'] for host in available_hosts]:
  96. ret['state'] = ji.Common.exchange_state(50351)
  97. return ret
  98. ssh_keys_id = request.json.get('ssh_keys_id', list())
  99. ssh_keys = list()
  100. ssh_key_guest_mapping = SSHKeyGuestMapping()
  101. if ssh_keys_id.__len__() > 0:
  102. rows, _ = SSHKey.get_by_filter(
  103. filter_str=':'.join(['id', 'in', ','.join(_id.__str__() for _id in ssh_keys_id)]))
  104. for row in rows:
  105. ssh_keys.append(row['public_key'])
  106. bandwidth = request.json.get('bandwidth')
  107. bandwidth_unit = request.json.get('bandwidth_unit')
  108. if bandwidth_unit == 'k':
  109. bandwidth = bandwidth * 1000
  110. elif bandwidth_unit == 'm':
  111. bandwidth = bandwidth * 1000 ** 2
  112. elif bandwidth_unit == 'g':
  113. bandwidth = bandwidth * 1000 ** 3
  114. else:
  115. ret = dict()
  116. ret['state'] = ji.Common.exchange_state(41203)
  117. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  118. quantity = request.json.get('quantity')
  119. while quantity:
  120. quantity -= 1
  121. guest = Guest()
  122. guest.uuid = uuid4().__str__()
  123. guest.cpu = request.json.get('cpu')
  124. # 虚拟机内存单位,模板生成方法中已置其为GiB
  125. guest.memory = request.json.get('memory')
  126. guest.bandwidth = bandwidth
  127. guest.os_template_image_id = request.json.get('os_template_image_id')
  128. guest.label = ji.Common.generate_random_code(length=8)
  129. guest.remark = request.json.get('remark', '')
  130. guest.password = request.json.get('password')
  131. if guest.password is None or guest.password.__len__() < 1:
  132. guest.password = ji.Common.generate_random_code(length=16)
  133. guest.ip = db.r.spop(app.config['ip_available_set'])
  134. db.r.sadd(app.config['ip_used_set'], guest.ip)
  135. guest.network = config.vm_network
  136. guest.manage_network = config.vm_manage_network
  137. guest.vnc_port = db.r.spop(app.config['vnc_port_available_set'])
  138. db.r.sadd(app.config['vnc_port_used_set'], guest.vnc_port)
  139. guest.vnc_password = ji.Common.generate_random_code(length=16)
  140. disk = Disk()
  141. disk.uuid = guest.uuid
  142. disk.remark = guest.label.__str__() + '_SystemImage'
  143. disk.format = 'qcow2'
  144. disk.sequence = 0
  145. disk.size = 0
  146. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  147. disk.guest_uuid = ''
  148. # disk.node_id 由 guest 事件处理机更新。涉及迁移时,其所属 node_id 会变更。参见 @models/event_processory.py:111 附近。
  149. disk.node_id = 0
  150. disk.quota(config=config)
  151. disk.create()
  152. # 在可用计算节点中平均分配任务
  153. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  154. guest.node_id = chosen_host['node_id']
  155. guest_xml = GuestXML(host=chosen_host, guest=guest, disk=disk, config=config,
  156. os_type=os_template_profile.os_type)
  157. guest.xml = guest_xml.get_domain()
  158. if node_id is not None:
  159. guest.node_id = node_id
  160. guest.node_id = int(guest.node_id)
  161. guest.create()
  162. ssh_key_guest_mapping.guest_uuid = guest.uuid
  163. if ssh_keys_id.__len__() > 0:
  164. for ssh_key_id in ssh_keys_id:
  165. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  166. ssh_key_guest_mapping.create()
  167. # 替换占位符为有效内容
  168. _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
  169. for k, v in enumerate(_os_template_initialize_operates):
  170. _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  171. replace('{HOSTNAME}', guest.label). \
  172. replace('{PASSWORD}', guest.password). \
  173. replace('{NETMASK}', config.netmask).\
  174. replace('{GATEWAY}', config.gateway).\
  175. replace('{DNS1}', config.dns1).\
  176. replace('{DNS2}', config.dns2). \
  177. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  178. _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
  179. replace('{HOSTNAME}', guest.label). \
  180. replace('{PASSWORD}', guest.password). \
  181. replace('{NETMASK}', config.netmask). \
  182. replace('{GATEWAY}', config.gateway). \
  183. replace('{DNS1}', config.dns1). \
  184. replace('{DNS2}', config.dns2). \
  185. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  186. message = {
  187. '_object': 'guest',
  188. 'action': 'create',
  189. 'uuid': guest.uuid,
  190. 'storage_mode': config.storage_mode,
  191. 'dfs_volume': config.dfs_volume,
  192. 'node_id': guest.node_id,
  193. 'name': guest.label,
  194. 'template_path': os_template_image.path,
  195. 'os_type': os_template_profile.os_type,
  196. 'disks': [disk.__dict__],
  197. 'xml': guest_xml.get_domain(),
  198. 'os_template_initialize_operates': _os_template_initialize_operates,
  199. 'passback_parameters': {}
  200. }
  201. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  202. return ret
  203. except ji.PreviewingError, e:
  204. return json.loads(e.message)
  205. @Utils.dumps2response
  206. def r_reboot(uuids):
  207. args_rules = [
  208. Rules.UUIDS.value
  209. ]
  210. try:
  211. ji.Check.previewing(args_rules, {'uuids': uuids})
  212. guest = Guest()
  213. for uuid in uuids.split(','):
  214. guest.uuid = uuid
  215. guest.get_by('uuid')
  216. for uuid in uuids.split(','):
  217. guest.uuid = uuid
  218. guest.get_by('uuid')
  219. message = {
  220. '_object': 'guest',
  221. 'action': 'reboot',
  222. 'uuid': uuid,
  223. 'node_id': guest.node_id
  224. }
  225. Utils.emit_instruction(message=json.dumps(message))
  226. ret = dict()
  227. ret['state'] = ji.Common.exchange_state(20000)
  228. return ret
  229. except ji.PreviewingError, e:
  230. return json.loads(e.message)
  231. @Utils.dumps2response
  232. def r_force_reboot(uuids):
  233. args_rules = [
  234. Rules.UUIDS.value
  235. ]
  236. try:
  237. ji.Check.previewing(args_rules, {'uuids': uuids})
  238. guest = Guest()
  239. for uuid in uuids.split(','):
  240. guest.uuid = uuid
  241. guest.get_by('uuid')
  242. for uuid in uuids.split(','):
  243. guest.uuid = uuid
  244. guest.get_by('uuid')
  245. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  246. message = {
  247. '_object': 'guest',
  248. 'action': 'force_reboot',
  249. 'uuid': uuid,
  250. 'node_id': guest.node_id,
  251. 'disks': disks
  252. }
  253. Utils.emit_instruction(message=json.dumps(message))
  254. ret = dict()
  255. ret['state'] = ji.Common.exchange_state(20000)
  256. return ret
  257. except ji.PreviewingError, e:
  258. return json.loads(e.message)
  259. @Utils.dumps2response
  260. def r_shutdown(uuids):
  261. args_rules = [
  262. Rules.UUIDS.value
  263. ]
  264. try:
  265. ji.Check.previewing(args_rules, {'uuids': uuids})
  266. guest = Guest()
  267. for uuid in uuids.split(','):
  268. guest.uuid = uuid
  269. guest.get_by('uuid')
  270. for uuid in uuids.split(','):
  271. guest.uuid = uuid
  272. guest.get_by('uuid')
  273. message = {
  274. '_object': 'guest',
  275. 'action': 'shutdown',
  276. 'uuid': uuid,
  277. 'node_id': guest.node_id
  278. }
  279. Utils.emit_instruction(message=json.dumps(message))
  280. ret = dict()
  281. ret['state'] = ji.Common.exchange_state(20000)
  282. return ret
  283. except ji.PreviewingError, e:
  284. return json.loads(e.message)
  285. @Utils.dumps2response
  286. def r_force_shutdown(uuids):
  287. args_rules = [
  288. Rules.UUIDS.value
  289. ]
  290. try:
  291. ji.Check.previewing(args_rules, {'uuids': uuids})
  292. guest = Guest()
  293. for uuid in uuids.split(','):
  294. guest.uuid = uuid
  295. guest.get_by('uuid')
  296. for uuid in uuids.split(','):
  297. guest.uuid = uuid
  298. guest.get_by('uuid')
  299. message = {
  300. '_object': 'guest',
  301. 'action': 'force_shutdown',
  302. 'uuid': uuid,
  303. 'node_id': guest.node_id
  304. }
  305. Utils.emit_instruction(message=json.dumps(message))
  306. ret = dict()
  307. ret['state'] = ji.Common.exchange_state(20000)
  308. return ret
  309. except ji.PreviewingError, e:
  310. return json.loads(e.message)
  311. @Utils.dumps2response
  312. def r_boot(uuids):
  313. # TODO: 做好关系依赖判断,比如boot不可以对suspend的实例操作。
  314. args_rules = [
  315. Rules.UUIDS.value
  316. ]
  317. try:
  318. ji.Check.previewing(args_rules, {'uuids': uuids})
  319. guest = Guest()
  320. for uuid in uuids.split(','):
  321. guest.uuid = uuid
  322. guest.get_by('uuid')
  323. config = Config()
  324. config.id = 1
  325. config.get()
  326. for uuid in uuids.split(','):
  327. guest.uuid = uuid
  328. guest.get_by('uuid')
  329. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  330. message = {
  331. '_object': 'guest',
  332. 'action': 'boot',
  333. 'uuid': uuid,
  334. 'node_id': guest.node_id,
  335. 'passback_parameters': {},
  336. 'disks': disks
  337. }
  338. Utils.emit_instruction(message=json.dumps(message))
  339. ret = dict()
  340. ret['state'] = ji.Common.exchange_state(20000)
  341. return ret
  342. except ji.PreviewingError, e:
  343. return json.loads(e.message)
  344. @Utils.dumps2response
  345. def r_suspend(uuids):
  346. args_rules = [
  347. Rules.UUIDS.value
  348. ]
  349. try:
  350. ji.Check.previewing(args_rules, {'uuids': uuids})
  351. guest = Guest()
  352. for uuid in uuids.split(','):
  353. guest.uuid = uuid
  354. guest.get_by('uuid')
  355. for uuid in uuids.split(','):
  356. guest.uuid = uuid
  357. guest.get_by('uuid')
  358. message = {
  359. '_object': 'guest',
  360. 'action': 'suspend',
  361. 'uuid': uuid,
  362. 'node_id': guest.node_id
  363. }
  364. Utils.emit_instruction(message=json.dumps(message))
  365. ret = dict()
  366. ret['state'] = ji.Common.exchange_state(20000)
  367. return ret
  368. except ji.PreviewingError, e:
  369. return json.loads(e.message)
  370. @Utils.dumps2response
  371. def r_resume(uuids):
  372. args_rules = [
  373. Rules.UUIDS.value
  374. ]
  375. try:
  376. ji.Check.previewing(args_rules, {'uuids': uuids})
  377. guest = Guest()
  378. for uuid in uuids.split(','):
  379. guest.uuid = uuid
  380. guest.get_by('uuid')
  381. for uuid in uuids.split(','):
  382. guest.uuid = uuid
  383. guest.get_by('uuid')
  384. message = {
  385. '_object': 'guest',
  386. 'action': 'resume',
  387. 'uuid': uuid,
  388. 'node_id': guest.node_id
  389. }
  390. Utils.emit_instruction(message=json.dumps(message))
  391. ret = dict()
  392. ret['state'] = ji.Common.exchange_state(20000)
  393. return ret
  394. except ji.PreviewingError, e:
  395. return json.loads(e.message)
  396. @Utils.dumps2response
  397. def r_delete(uuids):
  398. args_rules = [
  399. Rules.UUIDS.value
  400. ]
  401. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  402. try:
  403. ji.Check.previewing(args_rules, {'uuids': uuids})
  404. guest = Guest()
  405. # 检测所指定的 UUDIs 实例都存在
  406. for uuid in uuids.split(','):
  407. guest.uuid = uuid
  408. guest.get_by('uuid')
  409. config = Config()
  410. config.id = 1
  411. config.get()
  412. # 执行删除操作
  413. for uuid in uuids.split(','):
  414. guest.uuid = uuid
  415. guest.get_by('uuid')
  416. message = {
  417. '_object': 'guest',
  418. 'action': 'delete',
  419. 'uuid': uuid,
  420. 'storage_mode': config.storage_mode,
  421. 'dfs_volume': config.dfs_volume,
  422. 'node_id': guest.node_id
  423. }
  424. Utils.emit_instruction(message=json.dumps(message))
  425. # 删除创建失败的 Guest
  426. if guest.status == status.GuestState.dirty.value:
  427. disk = Disk()
  428. disk.uuid = guest.uuid
  429. disk.get_by('uuid')
  430. if disk.state == status.DiskState.pending.value:
  431. disk.delete()
  432. guest.delete()
  433. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  434. ret = dict()
  435. ret['state'] = ji.Common.exchange_state(20000)
  436. return ret
  437. except ji.PreviewingError, e:
  438. return json.loads(e.message)
  439. @Utils.dumps2response
  440. def r_attach_disk(uuid, disk_uuid):
  441. args_rules = [
  442. Rules.UUID.value,
  443. Rules.DISK_UUID.value
  444. ]
  445. try:
  446. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  447. guest = Guest()
  448. guest.uuid = uuid
  449. guest.get_by('uuid')
  450. disk = Disk()
  451. disk.uuid = disk_uuid
  452. disk.get_by('uuid')
  453. config = Config()
  454. config.id = 1
  455. config.get()
  456. ret = dict()
  457. ret['state'] = ji.Common.exchange_state(20000)
  458. # 判断欲挂载的磁盘是否空闲
  459. if disk.guest_uuid.__len__() > 0 or disk.state != DiskState.idle.value:
  460. ret['state'] = ji.Common.exchange_state(41258)
  461. return ret
  462. # 判断 Guest 是否处于可用状态
  463. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  464. ret['state'] = ji.Common.exchange_state(41259)
  465. return ret
  466. # 判断 Guest 与 磁盘是否在同一宿主机上
  467. if config.storage_mode in [status.StorageMode.local.value, status.StorageMode.shared_mount.value]:
  468. if guest.node_id != disk.node_id:
  469. ret['state'] = ji.Common.exchange_state(41260)
  470. return ret
  471. # 通过检测未被使用的序列,来确定当前磁盘在目标 Guest 身上的序列
  472. disk.guest_uuid = guest.uuid
  473. disks, count = disk.get_by_filter(filter_str='guest_uuid:in:' + guest.uuid)
  474. already_used_sequence = list()
  475. for _disk in disks:
  476. already_used_sequence.append(_disk['sequence'])
  477. for sequence in range(0, dev_table.__len__()):
  478. if sequence not in already_used_sequence:
  479. disk.sequence = sequence
  480. break
  481. disk.state = DiskState.mounting.value
  482. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  483. message = {
  484. '_object': 'guest',
  485. 'action': 'attach_disk',
  486. 'uuid': uuid,
  487. 'node_id': guest.node_id,
  488. 'xml': guest_xml.get_disk(),
  489. 'passback_parameters': {'disk_uuid': disk.uuid, 'sequence': disk.sequence},
  490. 'disks': [disk.__dict__]
  491. }
  492. Utils.emit_instruction(message=json.dumps(message))
  493. disk.update()
  494. return ret
  495. except ji.PreviewingError, e:
  496. return json.loads(e.message)
  497. @Utils.dumps2response
  498. def r_detach_disk(disk_uuid):
  499. args_rules = [
  500. Rules.DISK_UUID.value
  501. ]
  502. try:
  503. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  504. disk = Disk()
  505. disk.uuid = disk_uuid
  506. disk.get_by('uuid')
  507. ret = dict()
  508. ret['state'] = ji.Common.exchange_state(20000)
  509. if disk.state != DiskState.mounted.value or disk.sequence == 0:
  510. # 表示未被任何实例使用,已被分离
  511. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  512. # TODO: 系统盘单独范围其它状态
  513. return ret
  514. guest = Guest()
  515. guest.uuid = disk.guest_uuid
  516. guest.get_by('uuid')
  517. # 判断 Guest 是否处于可用状态
  518. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  519. ret['state'] = ji.Common.exchange_state(41259)
  520. return ret
  521. config = Config()
  522. config.id = 1
  523. config.get()
  524. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  525. message = {
  526. '_object': 'guest',
  527. 'action': 'detach_disk',
  528. 'uuid': disk.guest_uuid,
  529. 'node_id': guest.node_id,
  530. 'xml': guest_xml.get_disk(),
  531. 'passback_parameters': {'disk_uuid': disk.uuid}
  532. }
  533. Utils.emit_instruction(message=json.dumps(message))
  534. disk.state = DiskState.unloading.value
  535. disk.update()
  536. return ret
  537. except ji.PreviewingError, e:
  538. return json.loads(e.message)
  539. @Utils.dumps2response
  540. def r_migrate(uuids, destination_host):
  541. args_rules = [
  542. Rules.UUIDS.value,
  543. Rules.DESTINATION_HOST.value
  544. ]
  545. try:
  546. ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
  547. guest = Guest()
  548. for uuid in uuids.split(','):
  549. guest.uuid = uuid
  550. guest.get_by('uuid')
  551. config = Config()
  552. config.id = 1
  553. config.get()
  554. for uuid in uuids.split(','):
  555. guest.uuid = uuid
  556. guest.get_by('uuid')
  557. message = {
  558. '_object': 'guest',
  559. 'action': 'migrate',
  560. 'uuid': uuid,
  561. 'node_id': guest.node_id,
  562. 'storage_mode': config.storage_mode,
  563. 'duri': 'qemu+ssh://' + destination_host + '/system'
  564. }
  565. Utils.emit_instruction(message=json.dumps(message))
  566. ret = dict()
  567. ret['state'] = ji.Common.exchange_state(20000)
  568. return ret
  569. except ji.PreviewingError, e:
  570. return json.loads(e.message)
  571. @Utils.dumps2response
  572. def r_get(uuids):
  573. ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  574. if '200' != ret['state']['code']:
  575. return ret
  576. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', uuids]))
  577. guest_uuid_ssh_key_id_mapping = dict()
  578. ssh_keys_id = list()
  579. for row in rows:
  580. if row['ssh_key_id'] not in ssh_keys_id:
  581. ssh_keys_id.append(row['ssh_key_id'].__str__())
  582. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  583. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  584. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  585. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  586. ssh_key_id_mapping = dict()
  587. for row in rows:
  588. row['url'] = url_for('v_ssh_keys.show')
  589. ssh_key_id_mapping[row['id']] = row
  590. if -1 == uuids.find(','):
  591. if 'ssh_keys' not in ret['data']:
  592. ret['data']['ssh_keys'] = list()
  593. if ret['data']['uuid'] in guest_uuid_ssh_key_id_mapping:
  594. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data']['uuid']]:
  595. if ssh_key_id not in ssh_key_id_mapping:
  596. continue
  597. ret['data']['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  598. else:
  599. for i, guest in enumerate(ret['data']):
  600. if 'ssh_keys' not in ret['data'][i]:
  601. ret['data'][i]['ssh_keys'] = list()
  602. if ret['data'][i]['uuid'] in guest_uuid_ssh_key_id_mapping:
  603. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data'][i]['uuid']]:
  604. if ssh_key_id not in ssh_key_id_mapping:
  605. continue
  606. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  607. return ret
  608. @Utils.dumps2response
  609. def r_get_by_filter():
  610. ret = guest_base.get_by_filter()
  611. uuids = list()
  612. for guest in ret['data']:
  613. uuids.append(guest['uuid'])
  614. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  615. guest_uuid_ssh_key_id_mapping = dict()
  616. ssh_keys_id = list()
  617. for row in rows:
  618. if row['ssh_key_id'] not in ssh_keys_id:
  619. ssh_keys_id.append(row['ssh_key_id'].__str__())
  620. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  621. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  622. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  623. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  624. ssh_key_id_mapping = dict()
  625. for row in rows:
  626. row['url'] = url_for('v_ssh_keys.show')
  627. ssh_key_id_mapping[row['id']] = row
  628. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  629. snapshots_guest_uuid_mapping = dict()
  630. for row in rows:
  631. guest_uuid = row['guest_uuid']
  632. if guest_uuid not in snapshots_guest_uuid_mapping:
  633. snapshots_guest_uuid_mapping[guest_uuid] = list()
  634. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  635. for i, guest in enumerate(ret['data']):
  636. guest_uuid = ret['data'][i]['uuid']
  637. if 'ssh_keys' not in ret['data'][i]:
  638. ret['data'][i]['ssh_keys'] = list()
  639. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  640. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  641. if ssh_key_id not in ssh_key_id_mapping:
  642. continue
  643. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  644. if 'snapshot' not in ret['data'][i]:
  645. ret['data'][i]['snapshot'] = {
  646. 'creatable': True,
  647. 'mapping': list()
  648. }
  649. if guest_uuid in snapshots_guest_uuid_mapping:
  650. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  651. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  652. if snapshot['progress'] == 100:
  653. continue
  654. else:
  655. ret['data'][i]['snapshot']['creatable'] = False
  656. return ret
  657. @Utils.dumps2response
  658. def r_content_search():
  659. ret = guest_base.content_search()
  660. uuids = list()
  661. for guest in ret['data']:
  662. uuids.append(guest['uuid'])
  663. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  664. guest_uuid_ssh_key_id_mapping = dict()
  665. ssh_keys_id = list()
  666. for row in rows:
  667. if row['ssh_key_id'] not in ssh_keys_id:
  668. ssh_keys_id.append(row['ssh_key_id'].__str__())
  669. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  670. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  671. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  672. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  673. ssh_key_id_mapping = dict()
  674. for row in rows:
  675. row['url'] = url_for('v_ssh_keys.show')
  676. ssh_key_id_mapping[row['id']] = row
  677. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  678. snapshots_guest_uuid_mapping = dict()
  679. for row in rows:
  680. guest_uuid = row['guest_uuid']
  681. if guest_uuid not in snapshots_guest_uuid_mapping:
  682. snapshots_guest_uuid_mapping[guest_uuid] = list()
  683. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  684. for i, guest in enumerate(ret['data']):
  685. guest_uuid = ret['data'][i]['uuid']
  686. if 'ssh_keys' not in ret['data'][i]:
  687. ret['data'][i]['ssh_keys'] = list()
  688. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  689. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  690. if ssh_key_id not in ssh_key_id_mapping:
  691. continue
  692. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  693. if 'snapshot' not in ret['data'][i]:
  694. ret['data'][i]['snapshot'] = {
  695. 'creatable': True,
  696. 'mapping': list()
  697. }
  698. if guest_uuid in snapshots_guest_uuid_mapping:
  699. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  700. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  701. if snapshot['progress'] == 100:
  702. continue
  703. else:
  704. ret['data'][i]['snapshot']['creatable'] = False
  705. return ret
  706. @Utils.dumps2response
  707. def r_distribute_count():
  708. from models import Guest
  709. rows, count = Guest.get_all()
  710. ret = dict()
  711. ret['state'] = ji.Common.exchange_state(20000)
  712. ret['data'] = {
  713. 'os_template_image_id': dict(),
  714. 'status': dict(),
  715. 'node_id': dict(),
  716. 'cpu_memory': dict(),
  717. 'cpu': 0,
  718. 'memory': 0,
  719. 'guests': rows.__len__()
  720. }
  721. for guest in rows:
  722. if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
  723. ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
  724. if guest['status'] not in ret['data']['status']:
  725. ret['data']['status'][guest['status']] = 0
  726. if guest['node_id'] not in ret['data']['node_id']:
  727. ret['data']['node_id'][guest['node_id']] = 0
  728. cpu_memory = '_'.join([str(guest['cpu']), str(guest['memory'])])
  729. if cpu_memory not in ret['data']['cpu_memory']:
  730. ret['data']['cpu_memory'][cpu_memory] = 0
  731. ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
  732. ret['data']['status'][guest['status']] += 1
  733. ret['data']['node_id'][guest['node_id']] += 1
  734. ret['data']['cpu_memory'][cpu_memory] += 1
  735. ret['data']['cpu'] += guest['cpu']
  736. ret['data']['memory'] += guest['memory']
  737. return ret
  738. @Utils.dumps2response
  739. def r_update(uuid):
  740. args_rules = [
  741. Rules.UUID.value
  742. ]
  743. if 'remark' in request.json:
  744. args_rules.append(
  745. Rules.REMARK.value,
  746. )
  747. if args_rules.__len__() < 2:
  748. ret = dict()
  749. ret['state'] = ji.Common.exchange_state(20000)
  750. return ret
  751. request.json['uuid'] = uuid
  752. try:
  753. ji.Check.previewing(args_rules, request.json)
  754. guest = Guest()
  755. guest.uuid = uuid
  756. guest.get_by('uuid')
  757. guest.remark = request.json.get('remark', guest.label)
  758. guest.update()
  759. guest.get()
  760. ret = dict()
  761. ret['state'] = ji.Common.exchange_state(20000)
  762. ret['data'] = guest.__dict__
  763. return ret
  764. except ji.PreviewingError, e:
  765. return json.loads(e.message)
  766. @Utils.dumps2response
  767. def r_reset_password(uuids, password):
  768. args_rules = [
  769. Rules.UUIDS.value,
  770. Rules.PASSWORD.value
  771. ]
  772. try:
  773. ji.Check.previewing(args_rules, {'uuids': uuids, 'password': password})
  774. guest = Guest()
  775. os_template_image = OSTemplateImage()
  776. os_template_profile = OSTemplateProfile()
  777. # 检测所指定的 UUDIs 实例都存在
  778. for uuid in uuids.split(','):
  779. guest.uuid = uuid
  780. guest.get_by('uuid')
  781. for uuid in uuids.split(','):
  782. guest.uuid = uuid
  783. guest.get_by('uuid')
  784. os_template_image.id = guest.os_template_image_id
  785. os_template_image.get()
  786. os_template_profile.id = os_template_image.os_template_profile_id
  787. os_template_profile.get()
  788. user = 'root'
  789. if os_template_profile.os_type == 'windows':
  790. user = 'administrator'
  791. # guest.password 由 guest 事件处理机更新。参见 @models/event_processory.py:189 附近。
  792. message = {
  793. '_object': 'guest',
  794. 'action': 'reset_password',
  795. 'uuid': guest.uuid,
  796. 'node_id': guest.node_id,
  797. 'os_type': os_template_profile.os_type,
  798. 'user': user,
  799. 'password': password,
  800. 'passback_parameters': {'password': password}
  801. }
  802. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  803. ret = dict()
  804. ret['state'] = ji.Common.exchange_state(20000)
  805. return ret
  806. except ji.PreviewingError, e:
  807. return json.loads(e.message)
  808. @Utils.dumps2response
  809. def r_allocate_bandwidth(uuids, bandwidth, bandwidth_unit):
  810. args_rules = [
  811. Rules.UUIDS.value,
  812. Rules.BANDWIDTH_IN_URL.value,
  813. Rules.BANDWIDTH_UNIT.value,
  814. ]
  815. try:
  816. ji.Check.previewing(args_rules, {'uuids': uuids, 'bandwidth': bandwidth, 'bandwidth_unit': bandwidth_unit})
  817. bandwidth = int(bandwidth)
  818. if bandwidth_unit == 'k':
  819. bandwidth = bandwidth * 1000
  820. elif bandwidth_unit == 'm':
  821. bandwidth = bandwidth * 1000 ** 2
  822. elif bandwidth_unit == 'g':
  823. bandwidth = bandwidth * 1000 ** 3
  824. else:
  825. ret = dict()
  826. ret['state'] = ji.Common.exchange_state(41203)
  827. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  828. guest = Guest()
  829. # 检测所指定的 UUDIs 实例都存在
  830. for uuid in uuids.split(','):
  831. guest.uuid = uuid
  832. guest.get_by('uuid')
  833. for uuid in uuids.split(','):
  834. guest.uuid = uuid
  835. guest.get_by('uuid')
  836. guest.bandwidth = bandwidth
  837. message = {
  838. '_object': 'guest',
  839. 'action': 'allocate_bandwidth',
  840. 'uuid': guest.uuid,
  841. 'node_id': guest.node_id,
  842. 'bandwidth': guest.bandwidth,
  843. 'passback_parameters': {'bandwidth': guest.bandwidth}
  844. }
  845. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  846. ret = dict()
  847. ret['state'] = ji.Common.exchange_state(20000)
  848. return ret
  849. except ji.PreviewingError, e:
  850. return json.loads(e.message)