guest.py 21 KB

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