guest.py 27 KB

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