guest.py 21 KB

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