guest.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import copy
  4. from math import ceil
  5. from IPy import IP
  6. import requests
  7. import json
  8. from uuid import uuid4
  9. import random
  10. import time
  11. import jimit as ji
  12. from flask import Blueprint, url_for, request
  13. from api.base import Base
  14. from models.initialize import app, dev_table
  15. from models import DiskState, Host
  16. from models import Database as db
  17. from models import Config
  18. from models import Disk
  19. from models import Rules
  20. from models import Utils
  21. from models import Guest
  22. from models import OSTemplateImage
  23. from models import OSTemplateProfile
  24. from models import OSTemplateInitializeOperate
  25. from models import GuestXML
  26. from models import SSHKeyGuestMapping
  27. from models import SSHKey
  28. from models import Snapshot
  29. from models import status
  30. __author__ = 'James Iter'
  31. __date__ = '2017/3/22'
  32. __contact__ = 'james.iter.cn@gmail.com'
  33. __copyright__ = '(c) 2017 by James Iter.'
  34. blueprint = Blueprint(
  35. 'api_guest',
  36. __name__,
  37. url_prefix='/api/guest'
  38. )
  39. blueprints = Blueprint(
  40. 'api_guests',
  41. __name__,
  42. url_prefix='/api/guests'
  43. )
  44. guest_base = Base(the_class=Guest, the_blueprint=blueprint, the_blueprints=blueprints)
  45. os_template_image_base = Base(the_class=OSTemplateImage, the_blueprint=blueprint, the_blueprints=blueprints)
  46. os_template_profile_base = Base(the_class=OSTemplateProfile, the_blueprint=blueprint, the_blueprints=blueprints)
  47. @Utils.dumps2response
  48. def r_create():
  49. args_rules = [
  50. Rules.CPU.value,
  51. Rules.MEMORY.value,
  52. Rules.BANDWIDTH.value,
  53. Rules.BANDWIDTH_UNIT.value,
  54. Rules.OS_TEMPLATE_IMAGE_ID.value,
  55. Rules.QUANTITY.value,
  56. Rules.REMARK.value,
  57. Rules.PASSWORD.value,
  58. Rules.LEASE_TERM.value
  59. ]
  60. if 'node_id' in request.json:
  61. args_rules.append(
  62. Rules.NODE_ID.value
  63. )
  64. if 'ssh_keys_id' in request.json:
  65. args_rules.append(
  66. Rules.SSH_KEYS_ID.value
  67. )
  68. try:
  69. ret = dict()
  70. ret['state'] = ji.Common.exchange_state(20000)
  71. ji.Check.previewing(args_rules, request.json)
  72. config = Config()
  73. config.id = 1
  74. config.get()
  75. os_template_image = OSTemplateImage()
  76. os_template_profile = OSTemplateProfile()
  77. os_template_image.id = request.json.get('os_template_image_id')
  78. if not os_template_image.exist():
  79. ret['state'] = ji.Common.exchange_state(40450)
  80. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.id.__str__()])
  81. return ret
  82. os_template_image.get()
  83. os_template_profile.id = os_template_image.os_template_profile_id
  84. os_template_profile.get()
  85. os_template_initialize_operates, os_template_initialize_operates_count = \
  86. OSTemplateInitializeOperate.get_by_filter(
  87. filter_str='os_template_initialize_operate_set_id:eq:' +
  88. os_template_profile.os_template_initialize_operate_set_id.__str__())
  89. if db.r.scard(app.config['ip_available_set']) < 1:
  90. ret['state'] = ji.Common.exchange_state(50350)
  91. return ret
  92. node_id = request.json.get('node_id', None)
  93. # 默认只取可随机分配虚拟机的 hosts
  94. available_hosts = Host.get_available_hosts(nonrandom=False)
  95. # 当指定了 host 时,取全部活着的 hosts
  96. if node_id is not None:
  97. available_hosts = Host.get_available_hosts(nonrandom=None)
  98. if available_hosts.__len__() == 0:
  99. ret['state'] = ji.Common.exchange_state(50351)
  100. return ret
  101. available_hosts_mapping_by_node_id = dict()
  102. for host in available_hosts:
  103. if host['node_id'] not in available_hosts_mapping_by_node_id:
  104. available_hosts_mapping_by_node_id[host['node_id']] = host
  105. if node_id is not None and node_id not in available_hosts_mapping_by_node_id:
  106. ret['state'] = ji.Common.exchange_state(50351)
  107. return ret
  108. ssh_keys_id = request.json.get('ssh_keys_id', list())
  109. ssh_keys = list()
  110. ssh_key_guest_mapping = SSHKeyGuestMapping()
  111. if ssh_keys_id.__len__() > 0:
  112. rows, _ = SSHKey.get_by_filter(
  113. filter_str=':'.join(['id', 'in', ','.join(_id.__str__() for _id in ssh_keys_id)]))
  114. for row in rows:
  115. ssh_keys.append(row['public_key'])
  116. bandwidth = request.json.get('bandwidth')
  117. bandwidth_unit = request.json.get('bandwidth_unit')
  118. if bandwidth_unit == 'k':
  119. bandwidth = bandwidth * 1000
  120. elif bandwidth_unit == 'm':
  121. bandwidth = bandwidth * 1000 ** 2
  122. elif bandwidth_unit == 'g':
  123. bandwidth = bandwidth * 1000 ** 3
  124. else:
  125. ret = dict()
  126. ret['state'] = ji.Common.exchange_state(41203)
  127. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  128. # http://man7.org/linux/man-pages/man8/tc.8.html
  129. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  130. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  131. if bandwidth > 34359738360:
  132. bandwidth = 0
  133. quantity = request.json.get('quantity')
  134. while quantity:
  135. quantity -= 1
  136. guest = Guest()
  137. guest.uuid = uuid4().__str__()
  138. guest.cpu = request.json.get('cpu')
  139. # 虚拟机内存单位,模板生成方法中已置其为GiB
  140. guest.memory = request.json.get('memory')
  141. guest.bandwidth = bandwidth
  142. guest.os_template_image_id = request.json.get('os_template_image_id')
  143. guest.label = ji.Common.generate_random_code(length=8)
  144. guest.remark = request.json.get('remark', '')
  145. guest.password = request.json.get('password')
  146. if guest.password is None or guest.password.__len__() < 1:
  147. guest.password = ji.Common.generate_random_code(length=16)
  148. guest.ip = db.r.spop(app.config['ip_available_set'])
  149. db.r.sadd(app.config['ip_used_set'], guest.ip)
  150. guest.network = config.vm_network
  151. guest.manage_network = config.vm_manage_network
  152. guest.vnc_port = db.r.spop(app.config['vnc_port_available_set'])
  153. db.r.sadd(app.config['vnc_port_used_set'], guest.vnc_port)
  154. guest.vnc_password = ji.Common.generate_random_code(length=16)
  155. disk = Disk()
  156. disk.uuid = guest.uuid
  157. disk.remark = guest.label.__str__() + '_SystemImage'
  158. disk.format = 'qcow2'
  159. disk.sequence = 0
  160. disk.size = 0
  161. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  162. disk.guest_uuid = ''
  163. # disk.node_id 由 guest 事件处理机更新。涉及迁移时,其所属 node_id 会变更。参见 @models/event_processory.py:111 附近。
  164. disk.node_id = 0
  165. disk.quota(config=config)
  166. disk.create()
  167. if node_id is None:
  168. # 在可用计算节点中平均分配任务
  169. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  170. else:
  171. chosen_host = available_hosts_mapping_by_node_id[node_id]
  172. guest.node_id = chosen_host['node_id']
  173. guest_xml = GuestXML(host=chosen_host, guest=guest, disk=disk, config=config,
  174. os_type=os_template_profile.os_type)
  175. guest.xml = guest_xml.get_domain()
  176. guest.node_id = int(guest.node_id)
  177. guest.create()
  178. ssh_key_guest_mapping.guest_uuid = guest.uuid
  179. if ssh_keys_id.__len__() > 0:
  180. for ssh_key_id in ssh_keys_id:
  181. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  182. ssh_key_guest_mapping.create()
  183. if os_template_profile.os_distro == 'coreos':
  184. config.netmask = IP(guest.ip).make_net(config.netmask).prefixlen().__str__()
  185. # 替换占位符为有效内容
  186. _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
  187. for k, v in enumerate(_os_template_initialize_operates):
  188. _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  189. replace('{HOSTNAME}', guest.label). \
  190. replace('{PASSWORD}', guest.password). \
  191. replace('{NETMASK}', config.netmask).\
  192. replace('{GATEWAY}', config.gateway).\
  193. replace('{DNS1}', config.dns1).\
  194. replace('{DNS2}', config.dns2). \
  195. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  196. _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
  197. replace('{HOSTNAME}', guest.label). \
  198. replace('{PASSWORD}', guest.password). \
  199. replace('{NETMASK}', config.netmask). \
  200. replace('{GATEWAY}', config.gateway). \
  201. replace('{DNS1}', config.dns1). \
  202. replace('{DNS2}', config.dns2). \
  203. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  204. message = {
  205. '_object': 'guest',
  206. 'action': 'create',
  207. 'uuid': guest.uuid,
  208. 'storage_mode': config.storage_mode,
  209. 'dfs_volume': config.dfs_volume,
  210. 'node_id': guest.node_id,
  211. 'name': guest.label,
  212. 'template_path': os_template_image.path,
  213. 'os_type': os_template_profile.os_type,
  214. 'disks': [disk.__dict__],
  215. 'xml': guest_xml.get_domain(),
  216. 'os_template_initialize_operates': _os_template_initialize_operates,
  217. 'passback_parameters': {}
  218. }
  219. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  220. return ret
  221. except ji.PreviewingError, e:
  222. return json.loads(e.message)
  223. @Utils.dumps2response
  224. def r_reboot(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': 'reboot',
  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_reboot(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. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  264. message = {
  265. '_object': 'guest',
  266. 'action': 'force_reboot',
  267. 'uuid': uuid,
  268. 'node_id': guest.node_id,
  269. 'disks': disks
  270. }
  271. Utils.emit_instruction(message=json.dumps(message))
  272. ret = dict()
  273. ret['state'] = ji.Common.exchange_state(20000)
  274. return ret
  275. except ji.PreviewingError, e:
  276. return json.loads(e.message)
  277. @Utils.dumps2response
  278. def r_shutdown(uuids):
  279. args_rules = [
  280. Rules.UUIDS.value
  281. ]
  282. try:
  283. ji.Check.previewing(args_rules, {'uuids': uuids})
  284. guest = Guest()
  285. for uuid in uuids.split(','):
  286. guest.uuid = uuid
  287. guest.get_by('uuid')
  288. for uuid in uuids.split(','):
  289. guest.uuid = uuid
  290. guest.get_by('uuid')
  291. message = {
  292. '_object': 'guest',
  293. 'action': 'shutdown',
  294. 'uuid': uuid,
  295. 'node_id': guest.node_id
  296. }
  297. Utils.emit_instruction(message=json.dumps(message))
  298. ret = dict()
  299. ret['state'] = ji.Common.exchange_state(20000)
  300. return ret
  301. except ji.PreviewingError, e:
  302. return json.loads(e.message)
  303. @Utils.dumps2response
  304. def r_force_shutdown(uuids):
  305. args_rules = [
  306. Rules.UUIDS.value
  307. ]
  308. try:
  309. ji.Check.previewing(args_rules, {'uuids': uuids})
  310. guest = Guest()
  311. for uuid in uuids.split(','):
  312. guest.uuid = uuid
  313. guest.get_by('uuid')
  314. for uuid in uuids.split(','):
  315. guest.uuid = uuid
  316. guest.get_by('uuid')
  317. message = {
  318. '_object': 'guest',
  319. 'action': 'force_shutdown',
  320. 'uuid': uuid,
  321. 'node_id': guest.node_id
  322. }
  323. Utils.emit_instruction(message=json.dumps(message))
  324. ret = dict()
  325. ret['state'] = ji.Common.exchange_state(20000)
  326. return ret
  327. except ji.PreviewingError, e:
  328. return json.loads(e.message)
  329. @Utils.dumps2response
  330. def r_boot(uuids):
  331. # TODO: 做好关系依赖判断,比如boot不可以对suspend的实例操作。
  332. args_rules = [
  333. Rules.UUIDS.value
  334. ]
  335. try:
  336. ji.Check.previewing(args_rules, {'uuids': uuids})
  337. guest = Guest()
  338. for uuid in uuids.split(','):
  339. guest.uuid = uuid
  340. guest.get_by('uuid')
  341. config = Config()
  342. config.id = 1
  343. config.get()
  344. for uuid in uuids.split(','):
  345. guest.uuid = uuid
  346. guest.get_by('uuid')
  347. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  348. message = {
  349. '_object': 'guest',
  350. 'action': 'boot',
  351. 'uuid': uuid,
  352. 'node_id': guest.node_id,
  353. 'passback_parameters': {},
  354. 'disks': disks
  355. }
  356. Utils.emit_instruction(message=json.dumps(message))
  357. ret = dict()
  358. ret['state'] = ji.Common.exchange_state(20000)
  359. return ret
  360. except ji.PreviewingError, e:
  361. return json.loads(e.message)
  362. @Utils.dumps2response
  363. def r_suspend(uuids):
  364. args_rules = [
  365. Rules.UUIDS.value
  366. ]
  367. try:
  368. ji.Check.previewing(args_rules, {'uuids': uuids})
  369. guest = Guest()
  370. for uuid in uuids.split(','):
  371. guest.uuid = uuid
  372. guest.get_by('uuid')
  373. for uuid in uuids.split(','):
  374. guest.uuid = uuid
  375. guest.get_by('uuid')
  376. message = {
  377. '_object': 'guest',
  378. 'action': 'suspend',
  379. 'uuid': uuid,
  380. 'node_id': guest.node_id
  381. }
  382. Utils.emit_instruction(message=json.dumps(message))
  383. ret = dict()
  384. ret['state'] = ji.Common.exchange_state(20000)
  385. return ret
  386. except ji.PreviewingError, e:
  387. return json.loads(e.message)
  388. @Utils.dumps2response
  389. def r_resume(uuids):
  390. args_rules = [
  391. Rules.UUIDS.value
  392. ]
  393. try:
  394. ji.Check.previewing(args_rules, {'uuids': uuids})
  395. guest = Guest()
  396. for uuid in uuids.split(','):
  397. guest.uuid = uuid
  398. guest.get_by('uuid')
  399. for uuid in uuids.split(','):
  400. guest.uuid = uuid
  401. guest.get_by('uuid')
  402. message = {
  403. '_object': 'guest',
  404. 'action': 'resume',
  405. 'uuid': uuid,
  406. 'node_id': guest.node_id
  407. }
  408. Utils.emit_instruction(message=json.dumps(message))
  409. ret = dict()
  410. ret['state'] = ji.Common.exchange_state(20000)
  411. return ret
  412. except ji.PreviewingError, e:
  413. return json.loads(e.message)
  414. @Utils.dumps2response
  415. def r_delete(uuids):
  416. args_rules = [
  417. Rules.UUIDS.value
  418. ]
  419. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  420. try:
  421. ji.Check.previewing(args_rules, {'uuids': uuids})
  422. guest = Guest()
  423. # 检测所指定的 UUDIs 实例都存在
  424. for uuid in uuids.split(','):
  425. guest.uuid = uuid
  426. guest.get_by('uuid')
  427. config = Config()
  428. config.id = 1
  429. config.get()
  430. # 执行删除操作
  431. for uuid in uuids.split(','):
  432. guest.uuid = uuid
  433. guest.get_by('uuid')
  434. message = {
  435. '_object': 'guest',
  436. 'action': 'delete',
  437. 'uuid': uuid,
  438. 'storage_mode': config.storage_mode,
  439. 'dfs_volume': config.dfs_volume,
  440. 'node_id': guest.node_id
  441. }
  442. Utils.emit_instruction(message=json.dumps(message))
  443. # 删除创建失败的 Guest
  444. if guest.status == status.GuestState.dirty.value:
  445. disk = Disk()
  446. disk.uuid = guest.uuid
  447. disk.get_by('uuid')
  448. if disk.state == status.DiskState.pending.value:
  449. disk.delete()
  450. guest.delete()
  451. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  452. ret = dict()
  453. ret['state'] = ji.Common.exchange_state(20000)
  454. return ret
  455. except ji.PreviewingError, e:
  456. return json.loads(e.message)
  457. @Utils.dumps2response
  458. def r_attach_disk(uuid, disk_uuid):
  459. args_rules = [
  460. Rules.UUID.value,
  461. Rules.DISK_UUID.value
  462. ]
  463. try:
  464. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  465. guest = Guest()
  466. guest.uuid = uuid
  467. guest.get_by('uuid')
  468. disk = Disk()
  469. disk.uuid = disk_uuid
  470. disk.get_by('uuid')
  471. config = Config()
  472. config.id = 1
  473. config.get()
  474. ret = dict()
  475. ret['state'] = ji.Common.exchange_state(20000)
  476. # 判断欲挂载的磁盘是否空闲
  477. if disk.guest_uuid.__len__() > 0 or disk.state != DiskState.idle.value:
  478. ret['state'] = ji.Common.exchange_state(41258)
  479. return ret
  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. # 判断 Guest 与 磁盘是否在同一宿主机上
  485. if config.storage_mode in [status.StorageMode.local.value, status.StorageMode.shared_mount.value]:
  486. if guest.node_id != disk.node_id:
  487. ret['state'] = ji.Common.exchange_state(41260)
  488. return ret
  489. # 通过检测未被使用的序列,来确定当前磁盘在目标 Guest 身上的序列
  490. disk.guest_uuid = guest.uuid
  491. disks, count = disk.get_by_filter(filter_str='guest_uuid:in:' + guest.uuid)
  492. already_used_sequence = list()
  493. for _disk in disks:
  494. already_used_sequence.append(_disk['sequence'])
  495. for sequence in range(0, dev_table.__len__()):
  496. if sequence not in already_used_sequence:
  497. disk.sequence = sequence
  498. break
  499. disk.state = DiskState.mounting.value
  500. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  501. message = {
  502. '_object': 'guest',
  503. 'action': 'attach_disk',
  504. 'uuid': uuid,
  505. 'node_id': guest.node_id,
  506. 'xml': guest_xml.get_disk(),
  507. 'passback_parameters': {'disk_uuid': disk.uuid, 'sequence': disk.sequence},
  508. 'disks': [disk.__dict__]
  509. }
  510. Utils.emit_instruction(message=json.dumps(message))
  511. disk.update()
  512. return ret
  513. except ji.PreviewingError, e:
  514. return json.loads(e.message)
  515. @Utils.dumps2response
  516. def r_detach_disk(disk_uuid):
  517. args_rules = [
  518. Rules.DISK_UUID.value
  519. ]
  520. try:
  521. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  522. disk = Disk()
  523. disk.uuid = disk_uuid
  524. disk.get_by('uuid')
  525. ret = dict()
  526. ret['state'] = ji.Common.exchange_state(20000)
  527. if disk.state != DiskState.mounted.value or disk.sequence == 0:
  528. # 表示未被任何实例使用,已被分离
  529. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  530. # TODO: 系统盘单独范围其它状态
  531. return ret
  532. guest = Guest()
  533. guest.uuid = disk.guest_uuid
  534. guest.get_by('uuid')
  535. # 判断 Guest 是否处于可用状态
  536. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  537. ret['state'] = ji.Common.exchange_state(41259)
  538. return ret
  539. config = Config()
  540. config.id = 1
  541. config.get()
  542. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  543. message = {
  544. '_object': 'guest',
  545. 'action': 'detach_disk',
  546. 'uuid': disk.guest_uuid,
  547. 'node_id': guest.node_id,
  548. 'xml': guest_xml.get_disk(),
  549. 'passback_parameters': {'disk_uuid': disk.uuid}
  550. }
  551. Utils.emit_instruction(message=json.dumps(message))
  552. disk.state = DiskState.unloading.value
  553. disk.update()
  554. return ret
  555. except ji.PreviewingError, e:
  556. return json.loads(e.message)
  557. @Utils.dumps2response
  558. def r_migrate(uuids, destination_host):
  559. args_rules = [
  560. Rules.UUIDS.value,
  561. Rules.DESTINATION_HOST.value
  562. ]
  563. try:
  564. ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
  565. ret = dict()
  566. ret['state'] = ji.Common.exchange_state(20000)
  567. # 取全部活着的 hosts
  568. available_hosts = Host.get_available_hosts(nonrandom=None)
  569. if available_hosts.__len__() == 0:
  570. ret['state'] = ji.Common.exchange_state(50351)
  571. return ret
  572. available_hosts_mapping_by_node_id = dict()
  573. for host in available_hosts:
  574. if host['node_id'] not in available_hosts_mapping_by_node_id:
  575. available_hosts_mapping_by_node_id[host['node_id']] = host
  576. guest = Guest()
  577. for uuid in uuids.split(','):
  578. guest.uuid = uuid
  579. guest.get_by('uuid')
  580. config = Config()
  581. config.id = 1
  582. config.get()
  583. for uuid in uuids.split(','):
  584. guest.uuid = uuid
  585. guest.get_by('uuid')
  586. # 忽略宕机计算节点 上面的 虚拟机 迁移请求
  587. # 忽略目标计算节点 等于 当前所在 计算节点 的虚拟机 迁移请求
  588. if guest.node_id not in available_hosts_mapping_by_node_id or \
  589. available_hosts_mapping_by_node_id[guest.node_id]['hostname'] == destination_host:
  590. continue
  591. message = {
  592. '_object': 'guest',
  593. 'action': 'migrate',
  594. 'uuid': uuid,
  595. 'node_id': guest.node_id,
  596. 'storage_mode': config.storage_mode,
  597. 'duri': 'qemu+ssh://' + destination_host + '/system'
  598. }
  599. Utils.emit_instruction(message=json.dumps(message))
  600. return ret
  601. except ji.PreviewingError, e:
  602. return json.loads(e.message)
  603. @Utils.dumps2response
  604. def r_get(uuids):
  605. ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  606. if '200' != ret['state']['code']:
  607. return ret
  608. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', uuids]))
  609. guest_uuid_ssh_key_id_mapping = dict()
  610. ssh_keys_id = list()
  611. for row in rows:
  612. if row['ssh_key_id'] not in ssh_keys_id:
  613. ssh_keys_id.append(row['ssh_key_id'].__str__())
  614. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  615. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  616. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  617. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  618. ssh_key_id_mapping = dict()
  619. for row in rows:
  620. row['url'] = url_for('v_ssh_keys.show')
  621. ssh_key_id_mapping[row['id']] = row
  622. if -1 == uuids.find(','):
  623. if 'ssh_keys' not in ret['data']:
  624. ret['data']['ssh_keys'] = list()
  625. if ret['data']['uuid'] in guest_uuid_ssh_key_id_mapping:
  626. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data']['uuid']]:
  627. if ssh_key_id not in ssh_key_id_mapping:
  628. continue
  629. ret['data']['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  630. else:
  631. for i, guest in enumerate(ret['data']):
  632. if 'ssh_keys' not in ret['data'][i]:
  633. ret['data'][i]['ssh_keys'] = list()
  634. if ret['data'][i]['uuid'] in guest_uuid_ssh_key_id_mapping:
  635. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data'][i]['uuid']]:
  636. if ssh_key_id not in ssh_key_id_mapping:
  637. continue
  638. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  639. return ret
  640. @Utils.dumps2response
  641. def r_get_by_filter():
  642. ret = guest_base.get_by_filter()
  643. uuids = list()
  644. for guest in ret['data']:
  645. uuids.append(guest['uuid'])
  646. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  647. guest_uuid_ssh_key_id_mapping = dict()
  648. ssh_keys_id = list()
  649. for row in rows:
  650. if row['ssh_key_id'] not in ssh_keys_id:
  651. ssh_keys_id.append(row['ssh_key_id'].__str__())
  652. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  653. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  654. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  655. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  656. ssh_key_id_mapping = dict()
  657. for row in rows:
  658. row['url'] = url_for('v_ssh_keys.show')
  659. ssh_key_id_mapping[row['id']] = row
  660. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  661. snapshots_guest_uuid_mapping = dict()
  662. for row in rows:
  663. guest_uuid = row['guest_uuid']
  664. if guest_uuid not in snapshots_guest_uuid_mapping:
  665. snapshots_guest_uuid_mapping[guest_uuid] = list()
  666. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  667. for i, guest in enumerate(ret['data']):
  668. guest_uuid = ret['data'][i]['uuid']
  669. if 'ssh_keys' not in ret['data'][i]:
  670. ret['data'][i]['ssh_keys'] = list()
  671. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  672. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  673. if ssh_key_id not in ssh_key_id_mapping:
  674. continue
  675. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  676. if 'snapshot' not in ret['data'][i]:
  677. ret['data'][i]['snapshot'] = {
  678. 'creatable': True,
  679. 'mapping': list()
  680. }
  681. if guest_uuid in snapshots_guest_uuid_mapping:
  682. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  683. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  684. if snapshot['progress'] == 100:
  685. continue
  686. else:
  687. ret['data'][i]['snapshot']['creatable'] = False
  688. return ret
  689. @Utils.dumps2response
  690. def r_content_search():
  691. ret = guest_base.content_search()
  692. uuids = list()
  693. for guest in ret['data']:
  694. uuids.append(guest['uuid'])
  695. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  696. guest_uuid_ssh_key_id_mapping = dict()
  697. ssh_keys_id = list()
  698. for row in rows:
  699. if row['ssh_key_id'] not in ssh_keys_id:
  700. ssh_keys_id.append(row['ssh_key_id'].__str__())
  701. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  702. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  703. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  704. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  705. ssh_key_id_mapping = dict()
  706. for row in rows:
  707. row['url'] = url_for('v_ssh_keys.show')
  708. ssh_key_id_mapping[row['id']] = row
  709. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  710. snapshots_guest_uuid_mapping = dict()
  711. for row in rows:
  712. guest_uuid = row['guest_uuid']
  713. if guest_uuid not in snapshots_guest_uuid_mapping:
  714. snapshots_guest_uuid_mapping[guest_uuid] = list()
  715. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  716. for i, guest in enumerate(ret['data']):
  717. guest_uuid = ret['data'][i]['uuid']
  718. if 'ssh_keys' not in ret['data'][i]:
  719. ret['data'][i]['ssh_keys'] = list()
  720. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  721. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  722. if ssh_key_id not in ssh_key_id_mapping:
  723. continue
  724. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  725. if 'snapshot' not in ret['data'][i]:
  726. ret['data'][i]['snapshot'] = {
  727. 'creatable': True,
  728. 'mapping': list()
  729. }
  730. if guest_uuid in snapshots_guest_uuid_mapping:
  731. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  732. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  733. if snapshot['progress'] == 100:
  734. continue
  735. else:
  736. ret['data'][i]['snapshot']['creatable'] = False
  737. return ret
  738. @Utils.dumps2response
  739. def r_distribute_count():
  740. from models import Guest
  741. rows, count = Guest.get_all()
  742. ret = dict()
  743. ret['state'] = ji.Common.exchange_state(20000)
  744. ret['data'] = {
  745. 'os_template_image_id': dict(),
  746. 'status': dict(),
  747. 'node_id': dict(),
  748. 'cpu_memory': dict(),
  749. 'cpu': 0,
  750. 'memory': 0,
  751. 'guests': rows.__len__()
  752. }
  753. for guest in rows:
  754. if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
  755. ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
  756. if guest['status'] not in ret['data']['status']:
  757. ret['data']['status'][guest['status']] = 0
  758. if guest['node_id'] not in ret['data']['node_id']:
  759. ret['data']['node_id'][guest['node_id']] = 0
  760. cpu_memory = '_'.join([str(guest['cpu']), str(guest['memory'])])
  761. if cpu_memory not in ret['data']['cpu_memory']:
  762. ret['data']['cpu_memory'][cpu_memory] = 0
  763. ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
  764. ret['data']['status'][guest['status']] += 1
  765. ret['data']['node_id'][guest['node_id']] += 1
  766. ret['data']['cpu_memory'][cpu_memory] += 1
  767. ret['data']['cpu'] += guest['cpu']
  768. ret['data']['memory'] += guest['memory']
  769. return ret
  770. @Utils.dumps2response
  771. def r_update(uuid):
  772. args_rules = [
  773. Rules.UUID.value
  774. ]
  775. if 'remark' in request.json:
  776. args_rules.append(
  777. Rules.REMARK.value,
  778. )
  779. if args_rules.__len__() < 2:
  780. ret = dict()
  781. ret['state'] = ji.Common.exchange_state(20000)
  782. return ret
  783. request.json['uuid'] = uuid
  784. try:
  785. ji.Check.previewing(args_rules, request.json)
  786. guest = Guest()
  787. guest.uuid = uuid
  788. guest.get_by('uuid')
  789. guest.remark = request.json.get('remark', guest.label)
  790. guest.update()
  791. guest.get()
  792. ret = dict()
  793. ret['state'] = ji.Common.exchange_state(20000)
  794. ret['data'] = guest.__dict__
  795. return ret
  796. except ji.PreviewingError, e:
  797. return json.loads(e.message)
  798. @Utils.dumps2response
  799. def r_reset_password(uuids, password):
  800. args_rules = [
  801. Rules.UUIDS.value,
  802. Rules.PASSWORD.value
  803. ]
  804. try:
  805. ji.Check.previewing(args_rules, {'uuids': uuids, 'password': password})
  806. guest = Guest()
  807. os_template_image = OSTemplateImage()
  808. os_template_profile = OSTemplateProfile()
  809. # 检测所指定的 UUDIs 实例都存在
  810. for uuid in uuids.split(','):
  811. guest.uuid = uuid
  812. guest.get_by('uuid')
  813. for uuid in uuids.split(','):
  814. guest.uuid = uuid
  815. guest.get_by('uuid')
  816. os_template_image.id = guest.os_template_image_id
  817. os_template_image.get()
  818. os_template_profile.id = os_template_image.os_template_profile_id
  819. os_template_profile.get()
  820. user = 'root'
  821. if os_template_profile.os_type == 'windows':
  822. user = 'administrator'
  823. # guest.password 由 guest 事件处理机更新。参见 @models/event_processory.py:189 附近。
  824. message = {
  825. '_object': 'guest',
  826. 'action': 'reset_password',
  827. 'uuid': guest.uuid,
  828. 'node_id': guest.node_id,
  829. 'os_type': os_template_profile.os_type,
  830. 'user': user,
  831. 'password': password,
  832. 'passback_parameters': {'password': password}
  833. }
  834. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  835. ret = dict()
  836. ret['state'] = ji.Common.exchange_state(20000)
  837. return ret
  838. except ji.PreviewingError, e:
  839. return json.loads(e.message)
  840. @Utils.dumps2response
  841. def r_allocate_bandwidth(uuids, bandwidth, bandwidth_unit):
  842. args_rules = [
  843. Rules.UUIDS.value,
  844. Rules.BANDWIDTH_IN_URL.value,
  845. Rules.BANDWIDTH_UNIT.value,
  846. ]
  847. try:
  848. ji.Check.previewing(args_rules, {'uuids': uuids, 'bandwidth': bandwidth, 'bandwidth_unit': bandwidth_unit})
  849. ret = dict()
  850. ret['state'] = ji.Common.exchange_state(20000)
  851. bandwidth = int(bandwidth)
  852. if bandwidth_unit == 'k':
  853. bandwidth = bandwidth * 1000
  854. elif bandwidth_unit == 'm':
  855. bandwidth = bandwidth * 1000 ** 2
  856. elif bandwidth_unit == 'g':
  857. bandwidth = bandwidth * 1000 ** 3
  858. else:
  859. ret['state'] = ji.Common.exchange_state(41203)
  860. return ret
  861. # http://man7.org/linux/man-pages/man8/tc.8.html
  862. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  863. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  864. if bandwidth > 34359738360:
  865. bandwidth = 0
  866. guest = Guest()
  867. # 检测所指定的 UUDIs 实例都存在
  868. for uuid in uuids.split(','):
  869. guest.uuid = uuid
  870. guest.get_by('uuid')
  871. for uuid in uuids.split(','):
  872. guest.uuid = uuid
  873. guest.get_by('uuid')
  874. guest.bandwidth = bandwidth
  875. message = {
  876. '_object': 'guest',
  877. 'action': 'allocate_bandwidth',
  878. 'uuid': guest.uuid,
  879. 'node_id': guest.node_id,
  880. 'bandwidth': guest.bandwidth,
  881. 'passback_parameters': {'bandwidth': guest.bandwidth}
  882. }
  883. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  884. return ret
  885. except ji.PreviewingError, e:
  886. return json.loads(e.message)
  887. @Utils.dumps2response
  888. def r_adjust_ability(uuids, cpu, memory):
  889. args_rules = [
  890. Rules.UUIDS.value,
  891. Rules.CPU.value,
  892. Rules.MEMORY.value,
  893. ]
  894. try:
  895. ret = dict()
  896. ret['state'] = ji.Common.exchange_state(20000)
  897. cpu = int(cpu)
  898. memory = int(memory)
  899. ji.Check.previewing(args_rules, {'uuids': uuids, 'cpu': cpu, 'memory': memory})
  900. not_ready_yet_of_guests = list()
  901. guest = Guest()
  902. # 检测所指定的 UUDIs 实例都存在。且状态都为可以操作状态(即关闭状态)。
  903. for uuid in uuids.split(','):
  904. guest.uuid = uuid
  905. guest.get_by('uuid')
  906. if guest.status != status.GuestState.shutoff.value:
  907. not_ready_yet_of_guests.append(guest.__dict__)
  908. if not_ready_yet_of_guests.__len__() > 0:
  909. ret['state'] = ji.Common.exchange_state(41261)
  910. ret['data'] = not_ready_yet_of_guests
  911. return ret
  912. for uuid in uuids.split(','):
  913. guest.uuid = uuid
  914. guest.get_by('uuid')
  915. guest.cpu = cpu
  916. guest.memory = memory
  917. message = {
  918. '_object': 'guest',
  919. 'action': 'adjust_ability',
  920. 'uuid': guest.uuid,
  921. 'node_id': guest.node_id,
  922. 'cpu': guest.cpu,
  923. 'memory': guest.memory,
  924. 'passback_parameters': {'cpu': guest.cpu, 'memory': guest.memory}
  925. }
  926. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  927. return ret
  928. except ji.PreviewingError, e:
  929. return json.loads(e.message)
  930. @Utils.dumps2response
  931. def r_refresh_guest_state():
  932. try:
  933. ret = dict()
  934. ret['state'] = ji.Common.exchange_state(20000)
  935. # 取全部活着的 hosts
  936. available_hosts = Host.get_available_hosts(nonrandom=None)
  937. if available_hosts.__len__() == 0:
  938. ret['state'] = ji.Common.exchange_state(50351)
  939. return ret
  940. for host in available_hosts:
  941. message = {
  942. '_object': 'global',
  943. 'action': 'refresh_guest_state',
  944. 'node_id': host['node_id']
  945. }
  946. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  947. except ji.PreviewingError, e:
  948. return json.loads(e.message)
  949. @Utils.dumps2response
  950. def r_show():
  951. args = list()
  952. page = int(request.args.get('page', 1))
  953. page_size = int(request.args.get('page_size', 10))
  954. keyword = request.args.get('keyword', None)
  955. if page is not None:
  956. args.append('page=' + page.__str__())
  957. if page_size is not None:
  958. args.append('page_size=' + page_size.__str__())
  959. if keyword is not None:
  960. args.append('keyword=' + keyword.__str__())
  961. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  962. guests_url = url_for('api_guests.r_get_by_filter', _external=True)
  963. if keyword is not None:
  964. guests_url = url_for('api_guests.r_content_search', _external=True)
  965. if args.__len__() > 0:
  966. guests_url = guests_url + '?' + '&'.join(args)
  967. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies)
  968. hosts_ret = json.loads(hosts_ret.content)
  969. hosts_mapping_by_node_id = dict()
  970. for host in hosts_ret['data']:
  971. hosts_mapping_by_node_id[int(host['node_id'])] = host
  972. guests_ret = requests.get(url=guests_url, cookies=request.cookies)
  973. guests_ret = json.loads(guests_ret.content)
  974. os_templates_image, _ = OSTemplateImage.get_by_filter()
  975. os_templates_image_mapping_by_id = dict()
  976. for os_template_image in os_templates_image:
  977. os_templates_image_mapping_by_id[os_template_image['id']] = os_template_image
  978. os_templates_profile, _ = OSTemplateProfile.get_by_filter()
  979. os_templates_profile_mapping_by_id = dict()
  980. for os_template_profile in os_templates_profile:
  981. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  982. last_page = int(ceil(guests_ret['paging']['total'] / float(page_size)))
  983. page_length = 5
  984. pages = list()
  985. if page < int(ceil(page_length / 2.0)):
  986. for i in range(1, page_length + 1):
  987. pages.append(i)
  988. if i == last_page or last_page == 0:
  989. break
  990. elif last_page - page < page_length / 2:
  991. for i in range(last_page - page_length + 1, last_page + 1):
  992. if i < 1:
  993. continue
  994. pages.append(i)
  995. else:
  996. for i in range(page - page_length / 2, page + int(ceil(page_length / 2.0))):
  997. pages.append(i)
  998. if i == last_page or last_page == 0:
  999. break
  1000. ret = dict()
  1001. ret['state'] = ji.Common.exchange_state(20000)
  1002. ret['data'] = {
  1003. 'guests': guests_ret['data'],
  1004. 'os_templates_image_mapping_by_id': os_templates_image_mapping_by_id,
  1005. 'os_templates_profile_mapping_by_id': os_templates_profile_mapping_by_id,
  1006. 'hosts_mapping_by_node_id': hosts_mapping_by_node_id,
  1007. 'paging': guests_ret['paging'],
  1008. 'page': page,
  1009. 'page_size': page_size,
  1010. 'keyword': keyword,
  1011. 'pages': pages,
  1012. 'last_page': last_page
  1013. }
  1014. return ret
  1015. @Utils.dumps2response
  1016. def r_vnc(uuid):
  1017. guest_ret = guest_base.get(ids=uuid, ids_rule=Rules.UUID.value, by_field='uuid')
  1018. if '200' != guest_ret['state']['code']:
  1019. return guest_ret
  1020. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  1021. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies)
  1022. hosts_ret = json.loads(hosts_ret.content)
  1023. hosts_mapping_by_node_id = dict()
  1024. for host in hosts_ret['data']:
  1025. hosts_mapping_by_node_id[int(host['node_id'])] = host
  1026. port = random.randrange(50000, 60000)
  1027. while True:
  1028. if not Utils.port_is_opened(port=port):
  1029. break
  1030. port = random.randrange(50000, 60000)
  1031. payload = {'listen_port': port, 'target_host': hosts_mapping_by_node_id[guest_ret['data']['node_id']]['hostname'],
  1032. 'target_port': guest_ret['data']['vnc_port']}
  1033. db.r.rpush(app.config['ipc_queue'], json.dumps(payload, ensure_ascii=False))
  1034. time.sleep(1)
  1035. ret = dict()
  1036. ret['state'] = ji.Common.exchange_state(20000)
  1037. ret['data'] = {
  1038. 'port': port,
  1039. 'vnc_password': guest_ret['data']['vnc_password']
  1040. }
  1041. return ret
  1042. @Utils.dumps2response
  1043. def r_detail(uuid):
  1044. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  1045. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies)
  1046. hosts_ret = json.loads(hosts_ret.content)
  1047. hosts_mapping_by_node_id = dict()
  1048. for host in hosts_ret['data']:
  1049. hosts_mapping_by_node_id[int(host['node_id'])] = host
  1050. guest = Guest()
  1051. guest.uuid = uuid
  1052. guest.get_by(field='uuid')
  1053. guest.ssh_keys = list()
  1054. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', guest.uuid]))
  1055. ssh_keys_id = list()
  1056. for row in rows:
  1057. if row['ssh_key_id'] not in ssh_keys_id:
  1058. ssh_keys_id.append(row['ssh_key_id'].__str__())
  1059. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  1060. for row in rows:
  1061. row['url'] = url_for('v_ssh_keys.show')
  1062. guest.ssh_keys.append(row)
  1063. os_template_image = OSTemplateImage()
  1064. os_template_image.id = guest.os_template_image_id.__str__()
  1065. os_template_image.get()
  1066. os_template_profiles, _ = OSTemplateProfile.get_by_filter()
  1067. os_templates_profile_mapping_by_id = dict()
  1068. for os_template_profile in os_template_profiles:
  1069. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  1070. disks_url = url_for('api_disks.r_get_by_filter', filter='guest_uuid:in:' + guest.uuid, _external=True)
  1071. disks_ret = requests.get(url=disks_url, cookies=request.cookies)
  1072. disks = json.loads(disks_ret.content)['data']
  1073. config = Config()
  1074. config.id = 1
  1075. config.get()
  1076. ret = dict()
  1077. ret['state'] = ji.Common.exchange_state(20000)
  1078. ret['data'] = {
  1079. 'uuid': uuid,
  1080. 'guest': guest.__dict__,
  1081. 'os_template_image': os_template_image.__dict__,
  1082. 'os_templates_profile_mapping_by_id': os_templates_profile_mapping_by_id,
  1083. 'hosts_mapping_by_node_id': hosts_mapping_by_node_id,
  1084. 'disks': disks,
  1085. 'config': config.__dict__
  1086. }
  1087. return ret