guest.py 31 KB

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