guest.py 53 KB

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