guest.py 54 KB

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