guest.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983
  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 jimvc.api.base import Base
  14. from jimvc.models.initialize import dev_table
  15. from jimvc.models import app_config
  16. from jimvc.models import GuestState
  17. from jimvc.models import Service
  18. from jimvc.models import IPPool
  19. from jimvc.models import ReservedIP
  20. from jimvc.models import DiskState, Host
  21. from jimvc.models import Database as db
  22. from jimvc.models import Config
  23. from jimvc.models import Disk
  24. from jimvc.models import Rules
  25. from jimvc.models import Utils
  26. from jimvc.models import Guest
  27. from jimvc.models import OSTemplateImage
  28. from jimvc.models import OSTemplateProfile
  29. from jimvc.models import OSTemplateInitializeOperate
  30. from jimvc.models import GuestXML
  31. from jimvc.models import SSHKeyGuestMapping
  32. from jimvc.models import SSHKey
  33. from jimvc.models import Snapshot
  34. from jimvc.models import status
  35. from jimvc.models import VLAN
  36. __author__ = 'James Iter'
  37. __date__ = '2017/3/22'
  38. __contact__ = 'james.iter.cn@gmail.com'
  39. __copyright__ = '(c) 2017 by James Iter.'
  40. blueprint = Blueprint(
  41. 'api_guest',
  42. __name__,
  43. url_prefix='/api/guest'
  44. )
  45. blueprints = Blueprint(
  46. 'api_guests',
  47. __name__,
  48. url_prefix='/api/guests'
  49. )
  50. guest_base = Base(the_class=Guest, the_blueprint=blueprint, the_blueprints=blueprints)
  51. os_template_image_base = Base(the_class=OSTemplateImage, the_blueprint=blueprint, the_blueprints=blueprints)
  52. os_template_profile_base = Base(the_class=OSTemplateProfile, the_blueprint=blueprint, the_blueprints=blueprints)
  53. @Utils.dumps2response
  54. def r_create():
  55. args_rules = [
  56. Rules.CPU.value,
  57. Rules.MEMORY.value,
  58. Rules.BANDWIDTH.value,
  59. Rules.BANDWIDTH_UNIT.value,
  60. Rules.OS_TEMPLATE_IMAGE_ID.value,
  61. Rules.QUANTITY.value,
  62. Rules.REMARK.value,
  63. Rules.PASSWORD.value,
  64. Rules.LEASE_TERM.value
  65. ]
  66. if 'node_id' in request.json:
  67. args_rules.append(
  68. Rules.NODE_ID.value
  69. )
  70. if 'ssh_keys_id' in request.json:
  71. args_rules.append(
  72. Rules.SSH_KEYS_ID.value
  73. )
  74. if 'ip_pool_id' in request.json:
  75. args_rules.append(
  76. Rules.IP_POOL_ID.value
  77. )
  78. if 'service_id' in request.json:
  79. args_rules.append(
  80. Rules.SERVICE_ID.value
  81. )
  82. if 'autostart' in request.json:
  83. args_rules.append(
  84. Rules.AUTOSTART.value
  85. )
  86. if 'vlan_id' in request.json:
  87. args_rules.append(
  88. Rules.VLAN_ID.value
  89. )
  90. if 'link' in request.json:
  91. args_rules.append(
  92. Rules.LINK.value
  93. )
  94. try:
  95. ret = dict()
  96. ret['state'] = ji.Common.exchange_state(20000)
  97. ji.Check.previewing(args_rules, request.json)
  98. config = Config()
  99. config.id = 1
  100. config.get()
  101. os_template_image = OSTemplateImage()
  102. os_template_profile = OSTemplateProfile()
  103. os_template_image.id = request.json.get('os_template_image_id')
  104. if not os_template_image.exist():
  105. ret['state'] = ji.Common.exchange_state(40450)
  106. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.id.__str__()])
  107. return ret
  108. os_template_image.get()
  109. os_template_profile.id = os_template_image.os_template_profile_id
  110. os_template_profile.get()
  111. os_template_initialize_operates, os_template_initialize_operates_count = \
  112. OSTemplateInitializeOperate.get_by_filter(
  113. filter_str='os_template_initialize_operate_set_id:eq:' +
  114. os_template_profile.os_template_initialize_operate_set_id.__str__())
  115. node_id = request.json.get('node_id', None)
  116. # 默认只取可随机分配虚拟机的 hosts
  117. available_hosts = Host.get_available_hosts(nonrandom=False)
  118. # 当指定了 host 时,取全部活着的 hosts
  119. if node_id is not None:
  120. available_hosts = Host.get_available_hosts(nonrandom=None)
  121. if available_hosts.__len__() == 0:
  122. ret['state'] = ji.Common.exchange_state(50351)
  123. return ret
  124. available_hosts_mapping_by_node_id = dict()
  125. for host in available_hosts:
  126. if host['node_id'] not in available_hosts_mapping_by_node_id:
  127. available_hosts_mapping_by_node_id[host['node_id']] = host
  128. if node_id is not None and node_id not in available_hosts_mapping_by_node_id:
  129. ret['state'] = ji.Common.exchange_state(50351)
  130. return ret
  131. ssh_keys_id = request.json.get('ssh_keys_id', list())
  132. ssh_keys = list()
  133. ssh_key_guest_mapping = SSHKeyGuestMapping()
  134. if ssh_keys_id.__len__() > 0:
  135. rows, _ = SSHKey.get_by_filter(
  136. filter_str=':'.join(['id', 'in', ','.join(_id.__str__() for _id in ssh_keys_id)]))
  137. for row in rows:
  138. ssh_keys.append(row['public_key'])
  139. # 确保目标 服务组 存在
  140. service = Service()
  141. service.id = request.json.get('service_id', 1)
  142. service.get()
  143. bandwidth = request.json.get('bandwidth')
  144. bandwidth_unit = request.json.get('bandwidth_unit')
  145. if bandwidth_unit == 'k':
  146. bandwidth = bandwidth * 1000
  147. elif bandwidth_unit == 'm':
  148. bandwidth = bandwidth * 1000 ** 2
  149. elif bandwidth_unit == 'g':
  150. bandwidth = bandwidth * 1000 ** 3
  151. else:
  152. ret = dict()
  153. ret['state'] = ji.Common.exchange_state(41203)
  154. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  155. # http://man7.org/linux/man-pages/man8/tc.8.html
  156. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  157. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  158. if bandwidth > 34359738360:
  159. bandwidth = 0
  160. quantity = request.json.get('quantity')
  161. occupied_ips = list()
  162. occupied_vnc_ports = list()
  163. rows, count = Guest.get_all()
  164. for row in rows:
  165. occupied_ips.append(row['ip'])
  166. occupied_vnc_ports.append(row['vnc_port'])
  167. rows, count = ReservedIP.get_all()
  168. for row in rows:
  169. occupied_ips.append(row['ip'])
  170. ip_pool = IPPool()
  171. ip_pool.id = request.json.get('ip_pool_id', None)
  172. if ip_pool.id is None:
  173. rows, count = IPPool.get_by_filter(filter_str=':'.join(['activity', 'eq', '1']))
  174. if count < 1:
  175. ret['state'] = ji.Common.exchange_state(50350)
  176. return ret
  177. ip_pool.id = rows[0]['id']
  178. ip_pool.get()
  179. guest_ip_generator = ip_pool.ip_generator(occupied_ips=occupied_ips)
  180. guest_vnc_port_generator = ip_pool.vnc_port_generator(occupied_vnc_ports=occupied_vnc_ports)
  181. vlans, _ = VLAN.get_all()
  182. vlans_id = [-1]
  183. vlan_id = request.json.get('vlan_id', -1)
  184. for vlan in vlans:
  185. vlans_id.append(vlan['vlan_id'])
  186. if vlan_id not in vlans_id:
  187. ret['state'] = ji.Common.exchange_state(41263)
  188. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': vlan_id: ', vlan_id.__str__()])
  189. return ret
  190. while quantity:
  191. quantity -= 1
  192. guest = Guest()
  193. guest.uuid = uuid4().__str__()
  194. guest.cpu = request.json.get('cpu')
  195. # 虚拟机内存单位,模板生成方法中已置其为GiB
  196. guest.memory = request.json.get('memory')
  197. guest.bandwidth = bandwidth
  198. guest.os_template_image_id = request.json.get('os_template_image_id')
  199. guest.label = ji.Common.generate_random_code(length=8)
  200. guest.remark = request.json.get('remark', '')
  201. guest.autostart = request.json.get('autostart', False)
  202. guest.password = request.json.get('password')
  203. if guest.password is None or guest.password.__len__() < 1:
  204. guest.password = ji.Common.generate_random_code(length=16)
  205. guest.ip = guest_ip_generator.next()
  206. guest.link = request.json.get('link', 'http://' + guest.ip)
  207. guest.vnc_port = guest_vnc_port_generator.next()
  208. guest.vlan_id = vlan_id
  209. if guest.vlan_id == -1:
  210. guest.network = config.vm_network
  211. else:
  212. guest.network = 'vlan' + guest.vlan_id.__str__()
  213. guest.manage_network = config.vm_manage_network
  214. guest.vnc_password = ji.Common.generate_random_code(length=16)
  215. disk = Disk()
  216. disk.uuid = guest.uuid
  217. disk.remark = guest.label.__str__() + '_SystemImage'
  218. disk.format = 'qcow2'
  219. disk.sequence = 0
  220. disk.size = 0
  221. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  222. disk.guest_uuid = ''
  223. # disk.node_id 由 guest 事件处理机更新。涉及迁移时,其所属 node_id 会变更。参见 @models/event_processory.py:111 附近。
  224. disk.node_id = 0
  225. disk.quota(config=config)
  226. disk.create()
  227. if node_id is None:
  228. # 在可用计算节点中平均分配任务
  229. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  230. else:
  231. chosen_host = available_hosts_mapping_by_node_id[node_id]
  232. guest.node_id = chosen_host['node_id']
  233. guest.service_id = service.id
  234. guest_xml = GuestXML(host=chosen_host, guest=guest, disk=disk, config=config,
  235. os_type=os_template_profile.os_type)
  236. guest.xml = guest_xml.get_domain()
  237. guest.node_id = int(guest.node_id)
  238. guest.create()
  239. ssh_key_guest_mapping.guest_uuid = guest.uuid
  240. if ssh_keys_id.__len__() > 0:
  241. for ssh_key_id in ssh_keys_id:
  242. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  243. ssh_key_guest_mapping.create()
  244. if os_template_profile.os_distro == 'coreos':
  245. ip_pool.netmask = IP(guest.ip).make_net(ip_pool.netmask).prefixlen().__str__()
  246. # 替换占位符为有效内容
  247. _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
  248. for k, v in enumerate(_os_template_initialize_operates):
  249. _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  250. replace('{HOSTNAME}', guest.label). \
  251. replace('{PASSWORD}', guest.password). \
  252. replace('{NETMASK}', ip_pool.netmask).\
  253. replace('{GATEWAY}', ip_pool.gateway).\
  254. replace('{DNS1}', ip_pool.dns1).\
  255. replace('{DNS2}', ip_pool.dns2). \
  256. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  257. _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
  258. replace('{HOSTNAME}', guest.label). \
  259. replace('{PASSWORD}', guest.password). \
  260. replace('{NETMASK}', ip_pool.netmask). \
  261. replace('{GATEWAY}', ip_pool.gateway). \
  262. replace('{DNS1}', ip_pool.dns1). \
  263. replace('{DNS2}', ip_pool.dns2). \
  264. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  265. message = {
  266. '_object': 'guest',
  267. 'action': 'create',
  268. 'uuid': guest.uuid,
  269. 'storage_mode': config.storage_mode,
  270. 'dfs_volume': config.dfs_volume,
  271. 'node_id': guest.node_id,
  272. 'autostart': guest.autostart,
  273. 'name': guest.label,
  274. 'template_path': os_template_image.path,
  275. 'os_type': os_template_profile.os_type,
  276. 'vlan_id': guest.vlan_id,
  277. 'disks': [disk.__dict__],
  278. 'xml': guest_xml.get_domain(),
  279. 'os_template_initialize_operates': _os_template_initialize_operates,
  280. 'passback_parameters': {}
  281. }
  282. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  283. return ret
  284. except ji.PreviewingError, e:
  285. return json.loads(e.message)
  286. @Utils.dumps2response
  287. def r_autostart(uuids, autostart):
  288. args_rules = [
  289. Rules.UUIDS.value,
  290. Rules.AUTOSTART.value
  291. ]
  292. if str(autostart).lower() in ['false', '0']:
  293. autostart = False
  294. else:
  295. autostart = True
  296. try:
  297. ji.Check.previewing(args_rules, {'uuids': uuids, 'autostart': autostart})
  298. guest = Guest()
  299. for uuid in uuids.split(','):
  300. guest.uuid = uuid
  301. guest.get_by('uuid')
  302. for uuid in uuids.split(','):
  303. guest.uuid = uuid
  304. guest.get_by('uuid')
  305. message = {
  306. '_object': 'guest',
  307. 'action': 'autostart',
  308. 'uuid': uuid,
  309. 'node_id': guest.node_id,
  310. 'autostart': autostart,
  311. 'passback_parameters': {'autostart': autostart}
  312. }
  313. Utils.emit_instruction(message=json.dumps(message))
  314. ret = dict()
  315. ret['state'] = ji.Common.exchange_state(20000)
  316. return ret
  317. except ji.PreviewingError, e:
  318. return json.loads(e.message)
  319. @Utils.dumps2response
  320. def r_reboot(uuids):
  321. args_rules = [
  322. Rules.UUIDS.value
  323. ]
  324. try:
  325. ji.Check.previewing(args_rules, {'uuids': uuids})
  326. guest = Guest()
  327. for uuid in uuids.split(','):
  328. guest.uuid = uuid
  329. guest.get_by('uuid')
  330. for uuid in uuids.split(','):
  331. guest.uuid = uuid
  332. guest.get_by('uuid')
  333. message = {
  334. '_object': 'guest',
  335. 'action': 'reboot',
  336. 'uuid': uuid,
  337. 'node_id': guest.node_id
  338. }
  339. Utils.emit_instruction(message=json.dumps(message))
  340. ret = dict()
  341. ret['state'] = ji.Common.exchange_state(20000)
  342. return ret
  343. except ji.PreviewingError, e:
  344. return json.loads(e.message)
  345. @Utils.dumps2response
  346. def r_force_reboot(uuids):
  347. args_rules = [
  348. Rules.UUIDS.value
  349. ]
  350. try:
  351. ji.Check.previewing(args_rules, {'uuids': uuids})
  352. guest = Guest()
  353. for uuid in uuids.split(','):
  354. guest.uuid = uuid
  355. guest.get_by('uuid')
  356. for uuid in uuids.split(','):
  357. guest.uuid = uuid
  358. guest.get_by('uuid')
  359. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  360. message = {
  361. '_object': 'guest',
  362. 'action': 'force_reboot',
  363. 'uuid': uuid,
  364. 'node_id': guest.node_id,
  365. 'disks': disks
  366. }
  367. Utils.emit_instruction(message=json.dumps(message))
  368. ret = dict()
  369. ret['state'] = ji.Common.exchange_state(20000)
  370. return ret
  371. except ji.PreviewingError, e:
  372. return json.loads(e.message)
  373. @Utils.dumps2response
  374. def r_shutdown(uuids):
  375. args_rules = [
  376. Rules.UUIDS.value
  377. ]
  378. try:
  379. ji.Check.previewing(args_rules, {'uuids': uuids})
  380. guest = Guest()
  381. for uuid in uuids.split(','):
  382. guest.uuid = uuid
  383. guest.get_by('uuid')
  384. for uuid in uuids.split(','):
  385. guest.uuid = uuid
  386. guest.get_by('uuid')
  387. message = {
  388. '_object': 'guest',
  389. 'action': 'shutdown',
  390. 'uuid': uuid,
  391. 'node_id': guest.node_id
  392. }
  393. Utils.emit_instruction(message=json.dumps(message))
  394. ret = dict()
  395. ret['state'] = ji.Common.exchange_state(20000)
  396. return ret
  397. except ji.PreviewingError, e:
  398. return json.loads(e.message)
  399. @Utils.dumps2response
  400. def r_force_shutdown(uuids):
  401. args_rules = [
  402. Rules.UUIDS.value
  403. ]
  404. try:
  405. ji.Check.previewing(args_rules, {'uuids': uuids})
  406. guest = Guest()
  407. for uuid in uuids.split(','):
  408. guest.uuid = uuid
  409. guest.get_by('uuid')
  410. for uuid in uuids.split(','):
  411. guest.uuid = uuid
  412. guest.get_by('uuid')
  413. message = {
  414. '_object': 'guest',
  415. 'action': 'force_shutdown',
  416. 'uuid': uuid,
  417. 'node_id': guest.node_id
  418. }
  419. Utils.emit_instruction(message=json.dumps(message))
  420. ret = dict()
  421. ret['state'] = ji.Common.exchange_state(20000)
  422. return ret
  423. except ji.PreviewingError, e:
  424. return json.loads(e.message)
  425. @Utils.dumps2response
  426. def r_boot(uuids):
  427. # TODO: 做好关系依赖判断,比如boot不可以对suspend的实例操作。
  428. args_rules = [
  429. Rules.UUIDS.value
  430. ]
  431. try:
  432. ji.Check.previewing(args_rules, {'uuids': uuids})
  433. guest = Guest()
  434. for uuid in uuids.split(','):
  435. guest.uuid = uuid
  436. guest.get_by('uuid')
  437. config = Config()
  438. config.id = 1
  439. config.get()
  440. for uuid in uuids.split(','):
  441. guest.uuid = uuid
  442. guest.get_by('uuid')
  443. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  444. message = {
  445. '_object': 'guest',
  446. 'action': 'boot',
  447. 'uuid': uuid,
  448. 'node_id': guest.node_id,
  449. 'passback_parameters': {},
  450. 'disks': disks
  451. }
  452. Utils.emit_instruction(message=json.dumps(message))
  453. ret = dict()
  454. ret['state'] = ji.Common.exchange_state(20000)
  455. return ret
  456. except ji.PreviewingError, e:
  457. return json.loads(e.message)
  458. @Utils.dumps2response
  459. def r_suspend(uuids):
  460. args_rules = [
  461. Rules.UUIDS.value
  462. ]
  463. try:
  464. ji.Check.previewing(args_rules, {'uuids': uuids})
  465. guest = Guest()
  466. for uuid in uuids.split(','):
  467. guest.uuid = uuid
  468. guest.get_by('uuid')
  469. for uuid in uuids.split(','):
  470. guest.uuid = uuid
  471. guest.get_by('uuid')
  472. message = {
  473. '_object': 'guest',
  474. 'action': 'suspend',
  475. 'uuid': uuid,
  476. 'node_id': guest.node_id
  477. }
  478. Utils.emit_instruction(message=json.dumps(message))
  479. ret = dict()
  480. ret['state'] = ji.Common.exchange_state(20000)
  481. return ret
  482. except ji.PreviewingError, e:
  483. return json.loads(e.message)
  484. @Utils.dumps2response
  485. def r_resume(uuids):
  486. args_rules = [
  487. Rules.UUIDS.value
  488. ]
  489. try:
  490. ji.Check.previewing(args_rules, {'uuids': uuids})
  491. guest = Guest()
  492. for uuid in uuids.split(','):
  493. guest.uuid = uuid
  494. guest.get_by('uuid')
  495. for uuid in uuids.split(','):
  496. guest.uuid = uuid
  497. guest.get_by('uuid')
  498. message = {
  499. '_object': 'guest',
  500. 'action': 'resume',
  501. 'uuid': uuid,
  502. 'node_id': guest.node_id
  503. }
  504. Utils.emit_instruction(message=json.dumps(message))
  505. ret = dict()
  506. ret['state'] = ji.Common.exchange_state(20000)
  507. return ret
  508. except ji.PreviewingError, e:
  509. return json.loads(e.message)
  510. @Utils.dumps2response
  511. def r_delete(uuids):
  512. args_rules = [
  513. Rules.UUIDS.value
  514. ]
  515. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  516. try:
  517. ji.Check.previewing(args_rules, {'uuids': uuids})
  518. guest = Guest()
  519. # 检测所指定的 UUDIs 实例都存在
  520. for uuid in uuids.split(','):
  521. guest.uuid = uuid
  522. guest.get_by('uuid')
  523. config = Config()
  524. config.id = 1
  525. config.get()
  526. # 执行删除操作
  527. for uuid in uuids.split(','):
  528. guest.uuid = uuid
  529. guest.get_by('uuid')
  530. message = {
  531. '_object': 'guest',
  532. 'action': 'delete',
  533. 'uuid': uuid,
  534. 'storage_mode': config.storage_mode,
  535. 'dfs_volume': config.dfs_volume,
  536. 'node_id': guest.node_id
  537. }
  538. Utils.emit_instruction(message=json.dumps(message))
  539. # 删除创建失败的 Guest
  540. if guest.status == status.GuestState.dirty.value:
  541. disk = Disk()
  542. disk.uuid = guest.uuid
  543. disk.get_by('uuid')
  544. if disk.state == status.DiskState.pending.value:
  545. disk.delete()
  546. guest.delete()
  547. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  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_attach_disk(uuid, disk_uuid):
  555. args_rules = [
  556. Rules.UUID.value,
  557. Rules.DISK_UUID.value
  558. ]
  559. try:
  560. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  561. guest = Guest()
  562. guest.uuid = uuid
  563. guest.get_by('uuid')
  564. disk = Disk()
  565. disk.uuid = disk_uuid
  566. disk.get_by('uuid')
  567. config = Config()
  568. config.id = 1
  569. config.get()
  570. ret = dict()
  571. ret['state'] = ji.Common.exchange_state(20000)
  572. # 判断欲挂载的磁盘是否空闲
  573. if disk.guest_uuid.__len__() > 0 or disk.state != DiskState.idle.value:
  574. ret['state'] = ji.Common.exchange_state(41258)
  575. return ret
  576. # 判断 Guest 是否处于可用状态
  577. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  578. ret['state'] = ji.Common.exchange_state(41259)
  579. return ret
  580. # 判断 Guest 与 磁盘是否在同一宿主机上
  581. if config.storage_mode in [status.StorageMode.local.value, status.StorageMode.shared_mount.value]:
  582. if guest.node_id != disk.node_id:
  583. ret['state'] = ji.Common.exchange_state(41260)
  584. return ret
  585. # 通过检测未被使用的序列,来确定当前磁盘在目标 Guest 身上的序列
  586. disk.guest_uuid = guest.uuid
  587. disks, count = disk.get_by_filter(filter_str='guest_uuid:in:' + guest.uuid)
  588. already_used_sequence = list()
  589. for _disk in disks:
  590. already_used_sequence.append(_disk['sequence'])
  591. for sequence in range(0, dev_table.__len__()):
  592. if sequence not in already_used_sequence:
  593. disk.sequence = sequence
  594. break
  595. disk.state = DiskState.mounting.value
  596. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  597. message = {
  598. '_object': 'guest',
  599. 'action': 'attach_disk',
  600. 'uuid': uuid,
  601. 'node_id': guest.node_id,
  602. 'xml': guest_xml.get_disk(),
  603. 'passback_parameters': {'disk_uuid': disk.uuid, 'sequence': disk.sequence},
  604. 'disks': [disk.__dict__]
  605. }
  606. Utils.emit_instruction(message=json.dumps(message))
  607. disk.update()
  608. return ret
  609. except ji.PreviewingError, e:
  610. return json.loads(e.message)
  611. @Utils.dumps2response
  612. def r_detach_disk(disk_uuid):
  613. args_rules = [
  614. Rules.DISK_UUID.value
  615. ]
  616. try:
  617. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  618. disk = Disk()
  619. disk.uuid = disk_uuid
  620. disk.get_by('uuid')
  621. ret = dict()
  622. ret['state'] = ji.Common.exchange_state(20000)
  623. if disk.state != DiskState.mounted.value or disk.sequence == 0:
  624. # 表示未被任何实例使用,已被分离
  625. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  626. # TODO: 系统盘单独范围其它状态
  627. return ret
  628. guest = Guest()
  629. guest.uuid = disk.guest_uuid
  630. guest.get_by('uuid')
  631. # 判断 Guest 是否处于可用状态
  632. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  633. ret['state'] = ji.Common.exchange_state(41259)
  634. return ret
  635. config = Config()
  636. config.id = 1
  637. config.get()
  638. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  639. message = {
  640. '_object': 'guest',
  641. 'action': 'detach_disk',
  642. 'uuid': disk.guest_uuid,
  643. 'node_id': guest.node_id,
  644. 'xml': guest_xml.get_disk(),
  645. 'passback_parameters': {'disk_uuid': disk.uuid}
  646. }
  647. Utils.emit_instruction(message=json.dumps(message))
  648. disk.state = DiskState.unloading.value
  649. disk.update()
  650. return ret
  651. except ji.PreviewingError, e:
  652. return json.loads(e.message)
  653. @Utils.dumps2response
  654. def r_migrate(uuids, node_id):
  655. args_rules = [
  656. Rules.UUIDS.value,
  657. Rules.NODE_ID.value
  658. ]
  659. try:
  660. ji.Check.previewing(args_rules, {'uuids': uuids, 'node_id': node_id})
  661. ret = dict()
  662. ret['state'] = ji.Common.exchange_state(20000)
  663. config = Config()
  664. config.id = 1
  665. config.get()
  666. # 取全部活着的 hosts
  667. available_hosts = Host.get_available_hosts(nonrandom=None)
  668. if available_hosts.__len__() == 0:
  669. ret['state'] = ji.Common.exchange_state(50351)
  670. return ret
  671. available_hosts_mapping_by_node_id = dict()
  672. for host in available_hosts:
  673. if host['node_id'] not in available_hosts_mapping_by_node_id:
  674. available_hosts_mapping_by_node_id[host['node_id']] = host
  675. dst_ip = available_hosts_mapping_by_node_id[node_id]['interfaces'][config.vm_manage_network]['ip']
  676. guest = Guest()
  677. for uuid in uuids.split(','):
  678. guest.uuid = uuid
  679. guest.get_by('uuid')
  680. for uuid in uuids.split(','):
  681. guest.uuid = uuid
  682. guest.get_by('uuid')
  683. # 忽略宕机计算节点 上面的 虚拟机 迁移请求
  684. # 忽略目标计算节点 等于 当前所在 计算节点 的虚拟机 迁移请求
  685. if guest.node_id.__str__() not in available_hosts_mapping_by_node_id or guest.node_id.__str__() == node_id:
  686. continue
  687. message = {
  688. '_object': 'guest',
  689. 'action': 'migrate',
  690. 'uuid': uuid,
  691. 'node_id': guest.node_id,
  692. 'storage_mode': config.storage_mode,
  693. 'duri': 'qemu+ssh://' + dst_ip + '/system'
  694. }
  695. Utils.emit_instruction(message=json.dumps(message))
  696. return ret
  697. except ji.PreviewingError, e:
  698. return json.loads(e.message)
  699. @Utils.dumps2response
  700. def r_change_vlan(uuids, vlan_id):
  701. args_rules = [
  702. Rules.UUIDS.value,
  703. Rules.VLAN_ID_IN_URL.value
  704. ]
  705. try:
  706. ji.Check.previewing(args_rules, {'uuids': uuids, 'vlan_id': vlan_id})
  707. vlan_id = int(vlan_id)
  708. ret = dict()
  709. ret['state'] = ji.Common.exchange_state(20000)
  710. config = Config()
  711. config.id = 1
  712. config.get()
  713. guest = Guest()
  714. for uuid in uuids.split(','):
  715. guest.uuid = uuid
  716. guest.get_by('uuid')
  717. for uuid in uuids.split(','):
  718. guest.uuid = uuid
  719. guest.get_by('uuid')
  720. message = {
  721. '_object': 'guest',
  722. 'action': 'change_vlan',
  723. 'uuid': uuid,
  724. 'node_id': guest.node_id,
  725. 'vlan_id': vlan_id,
  726. 'vm_network': config.vm_network,
  727. 'passback_parameters': {'vlan_id': vlan_id}
  728. }
  729. Utils.emit_instruction(message=json.dumps(message))
  730. return ret
  731. except ji.PreviewingError, e:
  732. return json.loads(e.message)
  733. @Utils.dumps2response
  734. def r_get(uuids):
  735. ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  736. if '200' != ret['state']['code']:
  737. return ret
  738. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', uuids]))
  739. guest_uuid_ssh_key_id_mapping = dict()
  740. ssh_keys_id = list()
  741. for row in rows:
  742. if row['ssh_key_id'] not in ssh_keys_id:
  743. ssh_keys_id.append(row['ssh_key_id'].__str__())
  744. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  745. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  746. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  747. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  748. ssh_key_id_mapping = dict()
  749. for row in rows:
  750. row['url'] = url_for('v_ssh_keys.show')
  751. ssh_key_id_mapping[row['id']] = row
  752. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  753. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  754. hosts_ret = json.loads(hosts_ret.content)
  755. hosts_mapping_by_node_id = dict()
  756. for host in hosts_ret['data']:
  757. hosts_mapping_by_node_id[int(host['node_id'])] = host
  758. vlans_url = url_for('api_vlans.r_get_by_filter', _external=True)
  759. vlans_ret = requests.get(url=vlans_url, cookies=request.cookies, verify=False)
  760. vlans_ret = json.loads(vlans_ret.content)
  761. vlans_mapping_by_vlan_id = dict()
  762. for vlan in vlans_ret['data']:
  763. vlans_mapping_by_vlan_id[int(vlan['vlan_id'])] = vlan
  764. if -1 == uuids.find(','):
  765. if 'ssh_keys' not in ret['data']:
  766. ret['data']['ssh_keys'] = list()
  767. if ret['data']['uuid'] in guest_uuid_ssh_key_id_mapping:
  768. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data']['uuid']]:
  769. if ssh_key_id not in ssh_key_id_mapping:
  770. continue
  771. ret['data']['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  772. if ret['data']['node_id'] not in hosts_mapping_by_node_id or \
  773. not hosts_mapping_by_node_id[ret['data']['node_id']]['alive']:
  774. ret['data']['status'] = GuestState.no_state.value
  775. ret['data']['vlan'] = vlans_mapping_by_vlan_id[ret['data']['vlan_id']]
  776. else:
  777. for i, guest in enumerate(ret['data']):
  778. if 'ssh_keys' not in ret['data'][i]:
  779. ret['data'][i]['ssh_keys'] = list()
  780. if ret['data'][i]['uuid'] in guest_uuid_ssh_key_id_mapping:
  781. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data'][i]['uuid']]:
  782. if ssh_key_id not in ssh_key_id_mapping:
  783. continue
  784. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  785. if ret['data'][i]['node_id'] not in hosts_mapping_by_node_id or \
  786. not hosts_mapping_by_node_id[ret['data'][i]['node_id']]['alive']:
  787. ret['data'][i]['status'] = GuestState.no_state.value
  788. ret['data'][i]['vlan'] = vlans_mapping_by_vlan_id[ret['data'][i]['vlan_id']]
  789. return ret
  790. def exchange_guest_os_templates_logo(os_templates_image_mapping_by_id=None, os_templates_profile_mapping_by_id=None,
  791. os_template_image_id=None):
  792. assert isinstance(os_templates_image_mapping_by_id, dict)
  793. assert isinstance(os_templates_profile_mapping_by_id, dict)
  794. assert isinstance(os_template_image_id, int)
  795. if os_templates_image_mapping_by_id[os_template_image_id]['logo'] == "":
  796. logo = os_templates_profile_mapping_by_id[os_templates_image_mapping_by_id[os_template_image_id][
  797. 'os_template_profile_id']]['icon']
  798. else:
  799. logo = os_templates_image_mapping_by_id[os_template_image_id]['logo']
  800. label = os_templates_image_mapping_by_id[os_template_image_id]['label']
  801. return logo, label
  802. def format_guest_status(_status, progress):
  803. from jimvc.models import GuestState
  804. color = 'FF645B'
  805. icon = 'glyph-icon icon-bolt'
  806. desc = '未知状态'
  807. if _status == GuestState.booting.value:
  808. color = '00BBBB'
  809. icon = 'glyph-icon icon-circle'
  810. desc = '启动中'
  811. elif _status == GuestState.running.value:
  812. color = '00BB00'
  813. icon = 'glyph-icon icon-circle'
  814. desc = '运行中'
  815. elif _status == GuestState.creating.value:
  816. color = 'FFC543'
  817. icon = 'glyph-icon icon-spinner'
  818. desc = ' '.join(['创建中', str(progress) + '%'])
  819. elif _status == GuestState.blocked.value:
  820. color = '3D4245'
  821. icon = 'glyph-icon icon-minus-square'
  822. desc = '被阻塞'
  823. elif _status == GuestState.paused.value:
  824. color = 'B7B904'
  825. icon = 'glyph-icon icon-pause'
  826. desc = '暂停'
  827. elif _status == GuestState.shutdown.value:
  828. color = '4E5356'
  829. icon = 'glyph-icon icon-terminal'
  830. desc = '关闭'
  831. elif _status == GuestState.shutoff.value:
  832. color = 'FFC543'
  833. icon = 'glyph-icon icon-plug'
  834. desc = '断电'
  835. elif _status == GuestState.crashed.value:
  836. color = '9E2927'
  837. icon = 'glyph-icon icon-question'
  838. desc = '已崩溃'
  839. elif _status == GuestState.pm_suspended.value:
  840. color = 'FCFF07'
  841. icon = 'glyph-icon icon-anchor'
  842. desc = '悬挂'
  843. elif _status == GuestState.migrating.value:
  844. color = '1CF5E7'
  845. icon = 'glyph-icon icon-space-shuttle'
  846. desc = '迁移中'
  847. elif _status == GuestState.dirty.value:
  848. color = 'FF0707'
  849. icon = 'glyph-icon icon-remove'
  850. desc = '创建失败,待清理'
  851. else:
  852. pass
  853. return '<span class="{icon}" style="color: #{color};">&nbsp;&nbsp;{desc}</span>'.format(
  854. icon=icon, color=color, desc=desc)
  855. def exchange_guest_bandwidth(bandwidth=None):
  856. assert isinstance(bandwidth, int)
  857. if bandwidth == 0:
  858. bandwidth = '<span style="font-size: 16px;" title="无限带宽">&nbsp;∞</span>'
  859. elif 0 < bandwidth < 1000 ** 2:
  860. bandwidth = str(bandwidth // 1000) + ' Kbps'
  861. elif 1000 ** 2 <= bandwidth < 1000 ** 3:
  862. bandwidth = str(bandwidth // 1000 ** 2) + ' Mbps'
  863. else:
  864. bandwidth = str(bandwidth // 1000 ** 3) + ' Gbps'
  865. return bandwidth
  866. @Utils.dumps2response
  867. def r_get_by_filter():
  868. ret = guest_base.get_by_filter()
  869. uuids = list()
  870. for guest in ret['data']:
  871. uuids.append(guest['uuid'])
  872. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  873. guest_uuid_ssh_key_id_mapping = dict()
  874. ssh_keys_id = list()
  875. for row in rows:
  876. if row['ssh_key_id'] not in ssh_keys_id:
  877. ssh_keys_id.append(row['ssh_key_id'].__str__())
  878. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  879. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  880. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  881. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  882. ssh_key_id_mapping = dict()
  883. for row in rows:
  884. row['url'] = url_for('v_ssh_keys.show')
  885. ssh_key_id_mapping[row['id']] = row
  886. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  887. snapshots_guest_uuid_mapping = dict()
  888. for row in rows:
  889. guest_uuid = row['guest_uuid']
  890. if guest_uuid not in snapshots_guest_uuid_mapping:
  891. snapshots_guest_uuid_mapping[guest_uuid] = list()
  892. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  893. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  894. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  895. hosts_ret = json.loads(hosts_ret.content)
  896. hosts_mapping_by_node_id = dict()
  897. for host in hosts_ret['data']:
  898. hosts_mapping_by_node_id[int(host['node_id'])] = host
  899. vlans_url = url_for('api_vlans.r_get_by_filter', _external=True)
  900. vlans_ret = requests.get(url=vlans_url, cookies=request.cookies, verify=False)
  901. vlans_ret = json.loads(vlans_ret.content)
  902. vlans_mapping_by_vlan_id = dict()
  903. for vlan in vlans_ret['data']:
  904. vlans_mapping_by_vlan_id[int(vlan['vlan_id'])] = vlan
  905. os_templates_image, _ = OSTemplateImage.get_by_filter()
  906. os_templates_image_mapping_by_id = dict()
  907. for os_template_image in os_templates_image:
  908. os_templates_image_mapping_by_id[os_template_image['id']] = os_template_image
  909. os_templates_profile, _ = OSTemplateProfile.get_by_filter()
  910. os_templates_profile_mapping_by_id = dict()
  911. for os_template_profile in os_templates_profile:
  912. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  913. for i, guest in enumerate(ret['data']):
  914. guest_uuid = ret['data'][i]['uuid']
  915. if 'ssh_keys' not in ret['data'][i]:
  916. ret['data'][i]['ssh_keys'] = list()
  917. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  918. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  919. if ssh_key_id not in ssh_key_id_mapping:
  920. continue
  921. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  922. if 'snapshot' not in ret['data'][i]:
  923. ret['data'][i]['snapshot'] = {
  924. 'creatable': True,
  925. 'mapping': list()
  926. }
  927. if guest_uuid in snapshots_guest_uuid_mapping:
  928. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  929. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  930. if snapshot['progress'] == 100:
  931. continue
  932. else:
  933. ret['data'][i]['snapshot']['creatable'] = False
  934. if ret['data'][i]['node_id'] not in hosts_mapping_by_node_id or \
  935. not hosts_mapping_by_node_id[ret['data'][i]['node_id']]['alive']:
  936. ret['data'][i]['status'] = GuestState.no_state.value
  937. ret['data'][i]['hostname'] = hosts_mapping_by_node_id[guest['node_id']]['hostname'] \
  938. if guest['node_id'] in hosts_mapping_by_node_id else 'Unknown'
  939. ret['data'][i]['vlan'] = vlans_mapping_by_vlan_id[guest['vlan_id']]
  940. ret['data'][i]['html'] = dict()
  941. ret['data'][i]['html']['logo'], ret['data'][i]['html']['os_template_label'] = exchange_guest_os_templates_logo(
  942. os_templates_image_mapping_by_id=os_templates_image_mapping_by_id,
  943. os_templates_profile_mapping_by_id=os_templates_profile_mapping_by_id,
  944. os_template_image_id=guest['os_template_image_id'])
  945. ret['data'][i]['html']['status'] = format_guest_status(_status=guest['status'], progress=guest['progress'])
  946. ret['data'][i]['html']['bandwidth'] = exchange_guest_bandwidth(bandwidth=guest['bandwidth'])
  947. return ret
  948. @Utils.dumps2response
  949. def r_content_search():
  950. ret = guest_base.content_search()
  951. uuids = list()
  952. for guest in ret['data']:
  953. uuids.append(guest['uuid'])
  954. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  955. guest_uuid_ssh_key_id_mapping = dict()
  956. ssh_keys_id = list()
  957. for row in rows:
  958. if row['ssh_key_id'] not in ssh_keys_id:
  959. ssh_keys_id.append(row['ssh_key_id'].__str__())
  960. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  961. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  962. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  963. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  964. ssh_key_id_mapping = dict()
  965. for row in rows:
  966. row['url'] = url_for('v_ssh_keys.show')
  967. ssh_key_id_mapping[row['id']] = row
  968. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  969. snapshots_guest_uuid_mapping = dict()
  970. for row in rows:
  971. guest_uuid = row['guest_uuid']
  972. if guest_uuid not in snapshots_guest_uuid_mapping:
  973. snapshots_guest_uuid_mapping[guest_uuid] = list()
  974. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  975. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  976. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  977. hosts_ret = json.loads(hosts_ret.content)
  978. hosts_mapping_by_node_id = dict()
  979. for host in hosts_ret['data']:
  980. hosts_mapping_by_node_id[int(host['node_id'])] = host
  981. vlans_url = url_for('api_vlans.r_get_by_filter', _external=True)
  982. vlans_ret = requests.get(url=vlans_url, cookies=request.cookies, verify=False)
  983. vlans_ret = json.loads(vlans_ret.content)
  984. vlans_mapping_by_vlan_id = dict()
  985. for vlan in vlans_ret['data']:
  986. vlans_mapping_by_vlan_id[int(vlan['vlan_id'])] = vlan
  987. os_templates_image, _ = OSTemplateImage.get_by_filter()
  988. os_templates_image_mapping_by_id = dict()
  989. for os_template_image in os_templates_image:
  990. os_templates_image_mapping_by_id[os_template_image['id']] = os_template_image
  991. os_templates_profile, _ = OSTemplateProfile.get_by_filter()
  992. os_templates_profile_mapping_by_id = dict()
  993. for os_template_profile in os_templates_profile:
  994. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  995. for i, guest in enumerate(ret['data']):
  996. guest_uuid = ret['data'][i]['uuid']
  997. if 'ssh_keys' not in ret['data'][i]:
  998. ret['data'][i]['ssh_keys'] = list()
  999. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  1000. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  1001. if ssh_key_id not in ssh_key_id_mapping:
  1002. continue
  1003. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  1004. if 'snapshot' not in ret['data'][i]:
  1005. ret['data'][i]['snapshot'] = {
  1006. 'creatable': True,
  1007. 'mapping': list()
  1008. }
  1009. if guest_uuid in snapshots_guest_uuid_mapping:
  1010. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  1011. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  1012. if snapshot['progress'] == 100:
  1013. continue
  1014. else:
  1015. ret['data'][i]['snapshot']['creatable'] = False
  1016. if ret['data'][i]['node_id'] not in hosts_mapping_by_node_id or \
  1017. not hosts_mapping_by_node_id[ret['data'][i]['node_id']]['alive']:
  1018. ret['data'][i]['status'] = GuestState.no_state.value
  1019. ret['data'][i]['hostname'] = hosts_mapping_by_node_id[guest['node_id']]['hostname'] \
  1020. if guest['node_id'] in hosts_mapping_by_node_id else 'Unknown'
  1021. ret['data'][i]['vlan'] = vlans_mapping_by_vlan_id[guest['vlan_id']]
  1022. ret['data'][i]['html'] = dict()
  1023. ret['data'][i]['html']['logo'], ret['data'][i]['html']['os_template_label'] = exchange_guest_os_templates_logo(
  1024. os_templates_image_mapping_by_id=os_templates_image_mapping_by_id,
  1025. os_templates_profile_mapping_by_id=os_templates_profile_mapping_by_id,
  1026. os_template_image_id=guest['os_template_image_id'])
  1027. ret['data'][i]['html']['status'] = format_guest_status(_status=guest['status'], progress=guest['progress'])
  1028. ret['data'][i]['html']['bandwidth'] = exchange_guest_bandwidth(bandwidth=guest['bandwidth'])
  1029. return ret
  1030. @Utils.dumps2response
  1031. def r_distribute_count():
  1032. from jimvc.models import Guest
  1033. rows, count = Guest.get_all()
  1034. ret = dict()
  1035. ret['state'] = ji.Common.exchange_state(20000)
  1036. ret['data'] = {
  1037. 'os_template_image_id': dict(),
  1038. 'status': dict(),
  1039. 'node_id': dict(),
  1040. 'cpu_memory': dict(),
  1041. 'cpu': 0,
  1042. 'memory': 0,
  1043. 'guests': rows.__len__()
  1044. }
  1045. for guest in rows:
  1046. if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
  1047. ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
  1048. if guest['status'] not in ret['data']['status']:
  1049. ret['data']['status'][guest['status']] = 0
  1050. if guest['node_id'] not in ret['data']['node_id']:
  1051. ret['data']['node_id'][guest['node_id']] = 0
  1052. cpu_memory = '_'.join([str(guest['cpu']), str(guest['memory'])])
  1053. if cpu_memory not in ret['data']['cpu_memory']:
  1054. ret['data']['cpu_memory'][cpu_memory] = 0
  1055. ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
  1056. ret['data']['status'][guest['status']] += 1
  1057. ret['data']['node_id'][guest['node_id']] += 1
  1058. ret['data']['cpu_memory'][cpu_memory] += 1
  1059. ret['data']['cpu'] += guest['cpu']
  1060. ret['data']['memory'] += guest['memory']
  1061. return ret
  1062. @Utils.dumps2response
  1063. def r_update(uuids):
  1064. ret = dict()
  1065. ret['state'] = ji.Common.exchange_state(20000)
  1066. ret['data'] = list()
  1067. args_rules = [
  1068. Rules.UUIDS.value
  1069. ]
  1070. if 'remark' in request.json:
  1071. args_rules.append(
  1072. Rules.REMARK.value,
  1073. )
  1074. if 'link' in request.json:
  1075. args_rules.append(
  1076. Rules.LINK.value,
  1077. )
  1078. if args_rules.__len__() < 2:
  1079. return ret
  1080. request.json['uuids'] = uuids
  1081. try:
  1082. ji.Check.previewing(args_rules, request.json)
  1083. guest = Guest()
  1084. # 检测所指定的 UUDIs 实例都存在
  1085. for uuid in uuids.split(','):
  1086. guest.uuid = uuid
  1087. guest.get_by('uuid')
  1088. for uuid in uuids.split(','):
  1089. guest.uuid = uuid
  1090. guest.get_by('uuid')
  1091. guest.remark = request.json.get('remark', guest.remark)
  1092. guest.link = request.json.get('link', guest.link)
  1093. guest.update()
  1094. guest.get()
  1095. ret['data'].append(guest.__dict__)
  1096. return ret
  1097. except ji.PreviewingError, e:
  1098. return json.loads(e.message)
  1099. @Utils.dumps2response
  1100. def r_revise_ip(uuid, ip):
  1101. ret = dict()
  1102. ret['state'] = ji.Common.exchange_state(20000)
  1103. args_rules = [
  1104. Rules.UUID.value,
  1105. Rules.IP.value
  1106. ]
  1107. try:
  1108. ji.Check.previewing(args_rules, {'uuid': uuid, 'ip': ip})
  1109. guest = Guest()
  1110. guest.uuid = uuid
  1111. guest.get_by('uuid')
  1112. guest.ip = ip
  1113. guest.update()
  1114. guest.get()
  1115. ret['data'] = guest.__dict__
  1116. return ret
  1117. except ji.PreviewingError, e:
  1118. return json.loads(e.message)
  1119. @Utils.dumps2response
  1120. def r_reset_password(uuids, password):
  1121. args_rules = [
  1122. Rules.UUIDS.value,
  1123. Rules.PASSWORD.value
  1124. ]
  1125. try:
  1126. ji.Check.previewing(args_rules, {'uuids': uuids, 'password': password})
  1127. guest = Guest()
  1128. os_template_image = OSTemplateImage()
  1129. os_template_profile = OSTemplateProfile()
  1130. # 检测所指定的 UUDIs 实例都存在
  1131. for uuid in uuids.split(','):
  1132. guest.uuid = uuid
  1133. guest.get_by('uuid')
  1134. for uuid in uuids.split(','):
  1135. guest.uuid = uuid
  1136. guest.get_by('uuid')
  1137. os_template_image.id = guest.os_template_image_id
  1138. os_template_image.get()
  1139. os_template_profile.id = os_template_image.os_template_profile_id
  1140. os_template_profile.get()
  1141. user = 'root'
  1142. if os_template_profile.os_type == 'windows':
  1143. user = 'administrator'
  1144. # guest.password 由 guest 事件处理机更新。参见 @models/event_processory.py:189 附近。
  1145. message = {
  1146. '_object': 'guest',
  1147. 'action': 'reset_password',
  1148. 'uuid': guest.uuid,
  1149. 'node_id': guest.node_id,
  1150. 'os_type': os_template_profile.os_type,
  1151. 'user': user,
  1152. 'password': password,
  1153. 'passback_parameters': {'password': password}
  1154. }
  1155. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  1156. ret = dict()
  1157. ret['state'] = ji.Common.exchange_state(20000)
  1158. return ret
  1159. except ji.PreviewingError, e:
  1160. return json.loads(e.message)
  1161. @Utils.dumps2response
  1162. def r_allocate_bandwidth(uuids, bandwidth, bandwidth_unit):
  1163. args_rules = [
  1164. Rules.UUIDS.value,
  1165. Rules.BANDWIDTH_IN_URL.value,
  1166. Rules.BANDWIDTH_UNIT.value,
  1167. ]
  1168. try:
  1169. ji.Check.previewing(args_rules, {'uuids': uuids, 'bandwidth': bandwidth, 'bandwidth_unit': bandwidth_unit})
  1170. ret = dict()
  1171. ret['state'] = ji.Common.exchange_state(20000)
  1172. bandwidth = int(bandwidth)
  1173. if bandwidth_unit == 'k':
  1174. bandwidth = bandwidth * 1000
  1175. elif bandwidth_unit == 'm':
  1176. bandwidth = bandwidth * 1000 ** 2
  1177. elif bandwidth_unit == 'g':
  1178. bandwidth = bandwidth * 1000 ** 3
  1179. else:
  1180. ret['state'] = ji.Common.exchange_state(41203)
  1181. return ret
  1182. # http://man7.org/linux/man-pages/man8/tc.8.html
  1183. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  1184. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  1185. if bandwidth > 34359738360:
  1186. bandwidth = 0
  1187. guest = Guest()
  1188. # 检测所指定的 UUDIs 实例都存在
  1189. for uuid in uuids.split(','):
  1190. guest.uuid = uuid
  1191. guest.get_by('uuid')
  1192. for uuid in uuids.split(','):
  1193. guest.uuid = uuid
  1194. guest.get_by('uuid')
  1195. guest.bandwidth = bandwidth
  1196. message = {
  1197. '_object': 'guest',
  1198. 'action': 'allocate_bandwidth',
  1199. 'uuid': guest.uuid,
  1200. 'node_id': guest.node_id,
  1201. 'bandwidth': guest.bandwidth,
  1202. 'passback_parameters': {'bandwidth': guest.bandwidth}
  1203. }
  1204. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  1205. return ret
  1206. except ji.PreviewingError, e:
  1207. return json.loads(e.message)
  1208. @Utils.dumps2response
  1209. def r_adjust_ability(uuids, cpu, memory):
  1210. args_rules = [
  1211. Rules.UUIDS.value,
  1212. Rules.CPU.value,
  1213. Rules.MEMORY.value,
  1214. ]
  1215. try:
  1216. ret = dict()
  1217. ret['state'] = ji.Common.exchange_state(20000)
  1218. cpu = int(cpu)
  1219. memory = int(memory)
  1220. ji.Check.previewing(args_rules, {'uuids': uuids, 'cpu': cpu, 'memory': memory})
  1221. not_ready_yet_of_guests = list()
  1222. guest = Guest()
  1223. # 检测所指定的 UUDIs 实例都存在。且状态都为可以操作状态(即关闭状态)。
  1224. for uuid in uuids.split(','):
  1225. guest.uuid = uuid
  1226. guest.get_by('uuid')
  1227. if guest.status != status.GuestState.shutoff.value:
  1228. not_ready_yet_of_guests.append(guest.__dict__)
  1229. if not_ready_yet_of_guests.__len__() > 0:
  1230. ret['state'] = ji.Common.exchange_state(41261)
  1231. ret['data'] = not_ready_yet_of_guests
  1232. return ret
  1233. for uuid in uuids.split(','):
  1234. guest.uuid = uuid
  1235. guest.get_by('uuid')
  1236. guest.cpu = cpu
  1237. guest.memory = memory
  1238. message = {
  1239. '_object': 'guest',
  1240. 'action': 'adjust_ability',
  1241. 'uuid': guest.uuid,
  1242. 'node_id': guest.node_id,
  1243. 'cpu': guest.cpu,
  1244. 'memory': guest.memory,
  1245. 'passback_parameters': {'cpu': guest.cpu, 'memory': guest.memory}
  1246. }
  1247. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  1248. return ret
  1249. except ji.PreviewingError, e:
  1250. return json.loads(e.message)
  1251. @Utils.dumps2response
  1252. def r_change_prepared_by(uuids, service_id):
  1253. ret = dict()
  1254. ret['state'] = ji.Common.exchange_state(20000)
  1255. ret['data'] = list()
  1256. args_rules = [
  1257. Rules.UUIDS.value,
  1258. Rules.SERVICE_ID_IN_URL.value
  1259. ]
  1260. try:
  1261. ji.Check.previewing(args_rules, {'uuids': uuids, 'service_id': service_id})
  1262. guest = Guest()
  1263. # 检测所指定的 UUDIs 实例都存在
  1264. for uuid in uuids.split(','):
  1265. guest.uuid = uuid
  1266. guest.get_by('uuid')
  1267. for uuid in uuids.split(','):
  1268. guest.uuid = uuid
  1269. guest.get_by('uuid')
  1270. guest.service_id = int(service_id)
  1271. guest.update()
  1272. guest.get()
  1273. ret['data'].append(guest.__dict__)
  1274. return ret
  1275. except ji.PreviewingError, e:
  1276. return json.loads(e.message)
  1277. @Utils.dumps2response
  1278. def r_refresh_guest_state():
  1279. try:
  1280. ret = dict()
  1281. ret['state'] = ji.Common.exchange_state(20000)
  1282. # 取全部活着的 hosts
  1283. available_hosts = Host.get_available_hosts(nonrandom=None)
  1284. if available_hosts.__len__() == 0:
  1285. ret['state'] = ji.Common.exchange_state(50351)
  1286. return ret
  1287. for host in available_hosts:
  1288. message = {
  1289. '_object': 'global',
  1290. 'action': 'refresh_guest_state',
  1291. 'node_id': host['node_id']
  1292. }
  1293. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  1294. except ji.PreviewingError, e:
  1295. return json.loads(e.message)
  1296. @Utils.dumps2response
  1297. def r_show():
  1298. args = list()
  1299. page = int(request.args.get('page', 1))
  1300. page_size = int(request.args.get('page_size', 20))
  1301. order_by = request.args.get('order_by', 'id')
  1302. order = request.args.get('order', 'asc')
  1303. _filter = request.args.get('filter', None)
  1304. keyword = request.args.get('keyword', None)
  1305. if page is not None:
  1306. args.append('page=' + page.__str__())
  1307. if page_size is not None:
  1308. args.append('page_size=' + page_size.__str__())
  1309. if order_by is not None:
  1310. args.append('order_by=' + order_by.__str__())
  1311. if order is not None:
  1312. args.append('order=' + order.__str__())
  1313. if _filter is not None:
  1314. args.append('filter=' + _filter.__str__())
  1315. if keyword is not None:
  1316. args.append('keyword=' + keyword.__str__())
  1317. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  1318. guests_url = url_for('api_guests.r_get_by_filter', _external=True)
  1319. if keyword is not None:
  1320. guests_url = url_for('api_guests.r_content_search', _external=True)
  1321. if args.__len__() > 0:
  1322. guests_url = guests_url + '?' + '&'.join(args)
  1323. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  1324. hosts_ret = json.loads(hosts_ret.content)
  1325. hosts_mapping_by_node_id = dict()
  1326. for host in hosts_ret['data']:
  1327. hosts_mapping_by_node_id[int(host['node_id'])] = host
  1328. guests_ret = requests.get(url=guests_url, cookies=request.cookies, verify=False)
  1329. guests_ret = json.loads(guests_ret.content)
  1330. os_templates_image, _ = OSTemplateImage.get_by_filter()
  1331. os_templates_image_mapping_by_id = dict()
  1332. for os_template_image in os_templates_image:
  1333. os_templates_image_mapping_by_id[os_template_image['id']] = os_template_image
  1334. os_templates_profile, _ = OSTemplateProfile.get_by_filter()
  1335. os_templates_profile_mapping_by_id = dict()
  1336. for os_template_profile in os_templates_profile:
  1337. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  1338. last_page = int(ceil(guests_ret['paging']['total'] / float(page_size)))
  1339. page_length = 5
  1340. pages = list()
  1341. if page < int(ceil(page_length / 2.0)):
  1342. for i in range(1, page_length + 1):
  1343. pages.append(i)
  1344. if i == last_page or last_page == 0:
  1345. break
  1346. elif last_page - page < page_length / 2:
  1347. for i in range(last_page - page_length + 1, last_page + 1):
  1348. if i < 1:
  1349. continue
  1350. pages.append(i)
  1351. else:
  1352. for i in range(page - page_length / 2, page + int(ceil(page_length / 2.0))):
  1353. pages.append(i)
  1354. if i == last_page or last_page == 0:
  1355. break
  1356. ret = dict()
  1357. ret['state'] = ji.Common.exchange_state(20000)
  1358. ret['data'] = {
  1359. 'guests': guests_ret['data'],
  1360. 'os_templates_image_mapping_by_id': os_templates_image_mapping_by_id,
  1361. 'os_templates_profile_mapping_by_id': os_templates_profile_mapping_by_id,
  1362. 'hosts_mapping_by_node_id': hosts_mapping_by_node_id,
  1363. 'paging': guests_ret['paging'],
  1364. 'page': page,
  1365. 'page_size': page_size,
  1366. 'order_by': order_by,
  1367. 'order': order,
  1368. 'filter': _filter,
  1369. 'keyword': keyword,
  1370. 'pages': pages,
  1371. 'last_page': last_page
  1372. }
  1373. return ret
  1374. @Utils.dumps2response
  1375. def r_vnc(uuid):
  1376. guest_ret = guest_base.get(ids=uuid, ids_rule=Rules.UUID.value, by_field='uuid')
  1377. if '200' != guest_ret['state']['code']:
  1378. return guest_ret
  1379. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  1380. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  1381. hosts_ret = json.loads(hosts_ret.content)
  1382. hosts_mapping_by_node_id = dict()
  1383. for host in hosts_ret['data']:
  1384. hosts_mapping_by_node_id[int(host['node_id'])] = host
  1385. port = random.randrange(50000, 60000)
  1386. while True:
  1387. if not Utils.port_is_opened(port=port):
  1388. break
  1389. port = random.randrange(50000, 60000)
  1390. config = Config()
  1391. config.id = 1
  1392. config.get()
  1393. dst_ip = hosts_mapping_by_node_id[guest_ret['data']['node_id']]['interfaces'][config.vm_manage_network]['ip']
  1394. payload = {'listen_port': port,
  1395. 'target_host': dst_ip,
  1396. 'target_port': guest_ret['data']['vnc_port']}
  1397. db.r.rpush(app_config['ipc_queue'], json.dumps(payload, ensure_ascii=False))
  1398. time.sleep(1)
  1399. ret = dict()
  1400. ret['state'] = ji.Common.exchange_state(20000)
  1401. ret['data'] = {
  1402. 'port': port,
  1403. 'vnc_password': guest_ret['data']['vnc_password']
  1404. }
  1405. return ret
  1406. @Utils.dumps2response
  1407. def r_detail(uuid):
  1408. hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
  1409. hosts_ret = requests.get(url=hosts_url, cookies=request.cookies, verify=False)
  1410. hosts_ret = json.loads(hosts_ret.content)
  1411. hosts_mapping_by_node_id = dict()
  1412. for host in hosts_ret['data']:
  1413. hosts_mapping_by_node_id[int(host['node_id'])] = host
  1414. guest = Guest()
  1415. guest.uuid = uuid
  1416. guest.get_by(field='uuid')
  1417. guest.ssh_keys = list()
  1418. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', guest.uuid]))
  1419. ssh_keys_id = list()
  1420. for row in rows:
  1421. if row['ssh_key_id'] not in ssh_keys_id:
  1422. ssh_keys_id.append(row['ssh_key_id'].__str__())
  1423. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  1424. for row in rows:
  1425. row['url'] = url_for('v_ssh_keys.show')
  1426. if row['id'].__str__() not in ssh_keys_id:
  1427. continue
  1428. guest.ssh_keys.append(row)
  1429. os_template_image = OSTemplateImage()
  1430. os_template_image.id = guest.os_template_image_id.__str__()
  1431. os_template_image.get()
  1432. os_template_profiles, _ = OSTemplateProfile.get_by_filter()
  1433. os_templates_profile_mapping_by_id = dict()
  1434. for os_template_profile in os_template_profiles:
  1435. os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
  1436. disks_url = url_for('api_disks.r_get_by_filter', filter='guest_uuid:in:' + guest.uuid, _external=True)
  1437. disks_ret = requests.get(url=disks_url, cookies=request.cookies, verify=False)
  1438. disks = json.loads(disks_ret.content)['data']
  1439. if guest.node_id not in hosts_mapping_by_node_id or not hosts_mapping_by_node_id[guest.node_id]['alive']:
  1440. guest.status = GuestState.no_state.value
  1441. config = Config()
  1442. config.id = 1
  1443. config.get()
  1444. ret = dict()
  1445. ret['state'] = ji.Common.exchange_state(20000)
  1446. ret['data'] = {
  1447. 'uuid': uuid,
  1448. 'guest': guest.__dict__,
  1449. 'os_template_image': os_template_image.__dict__,
  1450. 'os_templates_profile_mapping_by_id': os_templates_profile_mapping_by_id,
  1451. 'hosts_mapping_by_node_id': hosts_mapping_by_node_id,
  1452. 'disks': disks,
  1453. 'config': config.__dict__
  1454. }
  1455. return ret