guest.py 58 KB

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