guest.py 56 KB

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