guest.py 28 KB

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