guest.py 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import copy
  4. from flask import Blueprint, url_for
  5. from flask import request
  6. import json
  7. from uuid import uuid4
  8. import jimit as ji
  9. from api.base import Base
  10. from models import DiskState, Host
  11. from models.initialize import app, dev_table
  12. from models import Database as db
  13. from models import Config
  14. from models import Disk
  15. from models import Rules
  16. from models import Utils
  17. from models import Guest
  18. from models import OSTemplateImage
  19. from models import OSTemplateProfile
  20. from models import OSTemplateInitializeOperate
  21. from models import GuestXML
  22. from models import SSHKeyGuestMapping
  23. from models import SSHKey
  24. from models import Snapshot
  25. from models import status
  26. __author__ = 'James Iter'
  27. __date__ = '2017/3/22'
  28. __contact__ = 'james.iter.cn@gmail.com'
  29. __copyright__ = '(c) 2017 by James Iter.'
  30. blueprint = Blueprint(
  31. 'api_guest',
  32. __name__,
  33. url_prefix='/api/guest'
  34. )
  35. blueprints = Blueprint(
  36. 'api_guests',
  37. __name__,
  38. url_prefix='/api/guests'
  39. )
  40. guest_base = Base(the_class=Guest, the_blueprint=blueprint, the_blueprints=blueprints)
  41. @Utils.dumps2response
  42. def r_create():
  43. args_rules = [
  44. Rules.CPU.value,
  45. Rules.MEMORY.value,
  46. Rules.BANDWIDTH.value,
  47. Rules.BANDWIDTH_UNIT.value,
  48. Rules.OS_TEMPLATE_IMAGE_ID.value,
  49. Rules.QUANTITY.value,
  50. Rules.REMARK.value,
  51. Rules.PASSWORD.value,
  52. Rules.LEASE_TERM.value
  53. ]
  54. if 'node_id' in request.json:
  55. args_rules.append(
  56. Rules.NODE_ID.value
  57. )
  58. if 'ssh_keys_id' in request.json:
  59. args_rules.append(
  60. Rules.SSH_KEYS_ID.value
  61. )
  62. try:
  63. ret = dict()
  64. ret['state'] = ji.Common.exchange_state(20000)
  65. ji.Check.previewing(args_rules, request.json)
  66. config = Config()
  67. config.id = 1
  68. config.get()
  69. os_template_image = OSTemplateImage()
  70. os_template_profile = OSTemplateProfile()
  71. os_template_image.id = request.json.get('os_template_image_id')
  72. if not os_template_image.exist():
  73. ret['state'] = ji.Common.exchange_state(40450)
  74. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.id.__str__()])
  75. return ret
  76. os_template_image.get()
  77. os_template_profile.id = os_template_image.os_template_profile_id
  78. os_template_profile.get()
  79. os_template_initialize_operates, os_template_initialize_operates_count = \
  80. OSTemplateInitializeOperate.get_by_filter(
  81. filter_str='os_template_initialize_operate_set_id:eq:' +
  82. os_template_profile.os_template_initialize_operate_set_id.__str__())
  83. if db.r.scard(app.config['ip_available_set']) < 1:
  84. ret['state'] = ji.Common.exchange_state(50350)
  85. return ret
  86. node_id = request.json.get('node_id', None)
  87. # 默认只取可随机分配虚拟机的 hosts
  88. available_hosts = Host.get_available_hosts(nonrandom=False)
  89. # 当指定了 host 时,取全部活着的 hosts
  90. if node_id is not None:
  91. available_hosts = Host.get_available_hosts(nonrandom=None)
  92. if available_hosts.__len__() == 0:
  93. ret['state'] = ji.Common.exchange_state(50351)
  94. return ret
  95. available_hosts_mapping_by_node_id = dict()
  96. for host in available_hosts:
  97. if host['node_id'] not in available_hosts_mapping_by_node_id:
  98. available_hosts_mapping_by_node_id[host['node_id']] = host
  99. if node_id is not None and node_id not in available_hosts_mapping_by_node_id:
  100. ret['state'] = ji.Common.exchange_state(50351)
  101. return ret
  102. ssh_keys_id = request.json.get('ssh_keys_id', list())
  103. ssh_keys = list()
  104. ssh_key_guest_mapping = SSHKeyGuestMapping()
  105. if ssh_keys_id.__len__() > 0:
  106. rows, _ = SSHKey.get_by_filter(
  107. filter_str=':'.join(['id', 'in', ','.join(_id.__str__() for _id in ssh_keys_id)]))
  108. for row in rows:
  109. ssh_keys.append(row['public_key'])
  110. bandwidth = request.json.get('bandwidth')
  111. bandwidth_unit = request.json.get('bandwidth_unit')
  112. if bandwidth_unit == 'k':
  113. bandwidth = bandwidth * 1000
  114. elif bandwidth_unit == 'm':
  115. bandwidth = bandwidth * 1000 ** 2
  116. elif bandwidth_unit == 'g':
  117. bandwidth = bandwidth * 1000 ** 3
  118. else:
  119. ret = dict()
  120. ret['state'] = ji.Common.exchange_state(41203)
  121. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  122. # http://man7.org/linux/man-pages/man8/tc.8.html
  123. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  124. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  125. if bandwidth > 34359738360:
  126. bandwidth = 0
  127. quantity = request.json.get('quantity')
  128. while quantity:
  129. quantity -= 1
  130. guest = Guest()
  131. guest.uuid = uuid4().__str__()
  132. guest.cpu = request.json.get('cpu')
  133. # 虚拟机内存单位,模板生成方法中已置其为GiB
  134. guest.memory = request.json.get('memory')
  135. guest.bandwidth = bandwidth
  136. guest.os_template_image_id = request.json.get('os_template_image_id')
  137. guest.label = ji.Common.generate_random_code(length=8)
  138. guest.remark = request.json.get('remark', '')
  139. guest.password = request.json.get('password')
  140. if guest.password is None or guest.password.__len__() < 1:
  141. guest.password = ji.Common.generate_random_code(length=16)
  142. guest.ip = db.r.spop(app.config['ip_available_set'])
  143. db.r.sadd(app.config['ip_used_set'], guest.ip)
  144. guest.network = config.vm_network
  145. guest.manage_network = config.vm_manage_network
  146. guest.vnc_port = db.r.spop(app.config['vnc_port_available_set'])
  147. db.r.sadd(app.config['vnc_port_used_set'], guest.vnc_port)
  148. guest.vnc_password = ji.Common.generate_random_code(length=16)
  149. disk = Disk()
  150. disk.uuid = guest.uuid
  151. disk.remark = guest.label.__str__() + '_SystemImage'
  152. disk.format = 'qcow2'
  153. disk.sequence = 0
  154. disk.size = 0
  155. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  156. disk.guest_uuid = ''
  157. # disk.node_id 由 guest 事件处理机更新。涉及迁移时,其所属 node_id 会变更。参见 @models/event_processory.py:111 附近。
  158. disk.node_id = 0
  159. disk.quota(config=config)
  160. disk.create()
  161. if node_id is None:
  162. # 在可用计算节点中平均分配任务
  163. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  164. else:
  165. chosen_host = available_hosts_mapping_by_node_id[node_id]
  166. guest.node_id = chosen_host['node_id']
  167. guest_xml = GuestXML(host=chosen_host, guest=guest, disk=disk, config=config,
  168. os_type=os_template_profile.os_type)
  169. guest.xml = guest_xml.get_domain()
  170. guest.node_id = int(guest.node_id)
  171. guest.create()
  172. ssh_key_guest_mapping.guest_uuid = guest.uuid
  173. if ssh_keys_id.__len__() > 0:
  174. for ssh_key_id in ssh_keys_id:
  175. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  176. ssh_key_guest_mapping.create()
  177. # 替换占位符为有效内容
  178. _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
  179. for k, v in enumerate(_os_template_initialize_operates):
  180. _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  181. replace('{HOSTNAME}', guest.label). \
  182. replace('{PASSWORD}', guest.password). \
  183. replace('{NETMASK}', config.netmask).\
  184. replace('{GATEWAY}', config.gateway).\
  185. replace('{DNS1}', config.dns1).\
  186. replace('{DNS2}', config.dns2). \
  187. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  188. _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
  189. replace('{HOSTNAME}', guest.label). \
  190. replace('{PASSWORD}', guest.password). \
  191. replace('{NETMASK}', config.netmask). \
  192. replace('{GATEWAY}', config.gateway). \
  193. replace('{DNS1}', config.dns1). \
  194. replace('{DNS2}', config.dns2). \
  195. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  196. message = {
  197. '_object': 'guest',
  198. 'action': 'create',
  199. 'uuid': guest.uuid,
  200. 'storage_mode': config.storage_mode,
  201. 'dfs_volume': config.dfs_volume,
  202. 'node_id': guest.node_id,
  203. 'name': guest.label,
  204. 'template_path': os_template_image.path,
  205. 'os_type': os_template_profile.os_type,
  206. 'disks': [disk.__dict__],
  207. 'xml': guest_xml.get_domain(),
  208. 'os_template_initialize_operates': _os_template_initialize_operates,
  209. 'passback_parameters': {}
  210. }
  211. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  212. return ret
  213. except ji.PreviewingError, e:
  214. return json.loads(e.message)
  215. @Utils.dumps2response
  216. def r_reboot(uuids):
  217. args_rules = [
  218. Rules.UUIDS.value
  219. ]
  220. try:
  221. ji.Check.previewing(args_rules, {'uuids': uuids})
  222. guest = Guest()
  223. for uuid in uuids.split(','):
  224. guest.uuid = uuid
  225. guest.get_by('uuid')
  226. for uuid in uuids.split(','):
  227. guest.uuid = uuid
  228. guest.get_by('uuid')
  229. message = {
  230. '_object': 'guest',
  231. 'action': 'reboot',
  232. 'uuid': uuid,
  233. 'node_id': guest.node_id
  234. }
  235. Utils.emit_instruction(message=json.dumps(message))
  236. ret = dict()
  237. ret['state'] = ji.Common.exchange_state(20000)
  238. return ret
  239. except ji.PreviewingError, e:
  240. return json.loads(e.message)
  241. @Utils.dumps2response
  242. def r_force_reboot(uuids):
  243. args_rules = [
  244. Rules.UUIDS.value
  245. ]
  246. try:
  247. ji.Check.previewing(args_rules, {'uuids': uuids})
  248. guest = Guest()
  249. for uuid in uuids.split(','):
  250. guest.uuid = uuid
  251. guest.get_by('uuid')
  252. for uuid in uuids.split(','):
  253. guest.uuid = uuid
  254. guest.get_by('uuid')
  255. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  256. message = {
  257. '_object': 'guest',
  258. 'action': 'force_reboot',
  259. 'uuid': uuid,
  260. 'node_id': guest.node_id,
  261. 'disks': disks
  262. }
  263. Utils.emit_instruction(message=json.dumps(message))
  264. ret = dict()
  265. ret['state'] = ji.Common.exchange_state(20000)
  266. return ret
  267. except ji.PreviewingError, e:
  268. return json.loads(e.message)
  269. @Utils.dumps2response
  270. def r_shutdown(uuids):
  271. args_rules = [
  272. Rules.UUIDS.value
  273. ]
  274. try:
  275. ji.Check.previewing(args_rules, {'uuids': uuids})
  276. guest = Guest()
  277. for uuid in uuids.split(','):
  278. guest.uuid = uuid
  279. guest.get_by('uuid')
  280. for uuid in uuids.split(','):
  281. guest.uuid = uuid
  282. guest.get_by('uuid')
  283. message = {
  284. '_object': 'guest',
  285. 'action': 'shutdown',
  286. 'uuid': uuid,
  287. 'node_id': guest.node_id
  288. }
  289. Utils.emit_instruction(message=json.dumps(message))
  290. ret = dict()
  291. ret['state'] = ji.Common.exchange_state(20000)
  292. return ret
  293. except ji.PreviewingError, e:
  294. return json.loads(e.message)
  295. @Utils.dumps2response
  296. def r_force_shutdown(uuids):
  297. args_rules = [
  298. Rules.UUIDS.value
  299. ]
  300. try:
  301. ji.Check.previewing(args_rules, {'uuids': uuids})
  302. guest = Guest()
  303. for uuid in uuids.split(','):
  304. guest.uuid = uuid
  305. guest.get_by('uuid')
  306. for uuid in uuids.split(','):
  307. guest.uuid = uuid
  308. guest.get_by('uuid')
  309. message = {
  310. '_object': 'guest',
  311. 'action': 'force_shutdown',
  312. 'uuid': uuid,
  313. 'node_id': guest.node_id
  314. }
  315. Utils.emit_instruction(message=json.dumps(message))
  316. ret = dict()
  317. ret['state'] = ji.Common.exchange_state(20000)
  318. return ret
  319. except ji.PreviewingError, e:
  320. return json.loads(e.message)
  321. @Utils.dumps2response
  322. def r_boot(uuids):
  323. # TODO: 做好关系依赖判断,比如boot不可以对suspend的实例操作。
  324. args_rules = [
  325. Rules.UUIDS.value
  326. ]
  327. try:
  328. ji.Check.previewing(args_rules, {'uuids': uuids})
  329. guest = Guest()
  330. for uuid in uuids.split(','):
  331. guest.uuid = uuid
  332. guest.get_by('uuid')
  333. config = Config()
  334. config.id = 1
  335. config.get()
  336. for uuid in uuids.split(','):
  337. guest.uuid = uuid
  338. guest.get_by('uuid')
  339. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  340. message = {
  341. '_object': 'guest',
  342. 'action': 'boot',
  343. 'uuid': uuid,
  344. 'node_id': guest.node_id,
  345. 'passback_parameters': {},
  346. 'disks': disks
  347. }
  348. Utils.emit_instruction(message=json.dumps(message))
  349. ret = dict()
  350. ret['state'] = ji.Common.exchange_state(20000)
  351. return ret
  352. except ji.PreviewingError, e:
  353. return json.loads(e.message)
  354. @Utils.dumps2response
  355. def r_suspend(uuids):
  356. args_rules = [
  357. Rules.UUIDS.value
  358. ]
  359. try:
  360. ji.Check.previewing(args_rules, {'uuids': uuids})
  361. guest = Guest()
  362. for uuid in uuids.split(','):
  363. guest.uuid = uuid
  364. guest.get_by('uuid')
  365. for uuid in uuids.split(','):
  366. guest.uuid = uuid
  367. guest.get_by('uuid')
  368. message = {
  369. '_object': 'guest',
  370. 'action': 'suspend',
  371. 'uuid': uuid,
  372. 'node_id': guest.node_id
  373. }
  374. Utils.emit_instruction(message=json.dumps(message))
  375. ret = dict()
  376. ret['state'] = ji.Common.exchange_state(20000)
  377. return ret
  378. except ji.PreviewingError, e:
  379. return json.loads(e.message)
  380. @Utils.dumps2response
  381. def r_resume(uuids):
  382. args_rules = [
  383. Rules.UUIDS.value
  384. ]
  385. try:
  386. ji.Check.previewing(args_rules, {'uuids': uuids})
  387. guest = Guest()
  388. for uuid in uuids.split(','):
  389. guest.uuid = uuid
  390. guest.get_by('uuid')
  391. for uuid in uuids.split(','):
  392. guest.uuid = uuid
  393. guest.get_by('uuid')
  394. message = {
  395. '_object': 'guest',
  396. 'action': 'resume',
  397. 'uuid': uuid,
  398. 'node_id': guest.node_id
  399. }
  400. Utils.emit_instruction(message=json.dumps(message))
  401. ret = dict()
  402. ret['state'] = ji.Common.exchange_state(20000)
  403. return ret
  404. except ji.PreviewingError, e:
  405. return json.loads(e.message)
  406. @Utils.dumps2response
  407. def r_delete(uuids):
  408. args_rules = [
  409. Rules.UUIDS.value
  410. ]
  411. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  412. try:
  413. ji.Check.previewing(args_rules, {'uuids': uuids})
  414. guest = Guest()
  415. # 检测所指定的 UUDIs 实例都存在
  416. for uuid in uuids.split(','):
  417. guest.uuid = uuid
  418. guest.get_by('uuid')
  419. config = Config()
  420. config.id = 1
  421. config.get()
  422. # 执行删除操作
  423. for uuid in uuids.split(','):
  424. guest.uuid = uuid
  425. guest.get_by('uuid')
  426. message = {
  427. '_object': 'guest',
  428. 'action': 'delete',
  429. 'uuid': uuid,
  430. 'storage_mode': config.storage_mode,
  431. 'dfs_volume': config.dfs_volume,
  432. 'node_id': guest.node_id
  433. }
  434. Utils.emit_instruction(message=json.dumps(message))
  435. # 删除创建失败的 Guest
  436. if guest.status == status.GuestState.dirty.value:
  437. disk = Disk()
  438. disk.uuid = guest.uuid
  439. disk.get_by('uuid')
  440. if disk.state == status.DiskState.pending.value:
  441. disk.delete()
  442. guest.delete()
  443. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  444. ret = dict()
  445. ret['state'] = ji.Common.exchange_state(20000)
  446. return ret
  447. except ji.PreviewingError, e:
  448. return json.loads(e.message)
  449. @Utils.dumps2response
  450. def r_attach_disk(uuid, disk_uuid):
  451. args_rules = [
  452. Rules.UUID.value,
  453. Rules.DISK_UUID.value
  454. ]
  455. try:
  456. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  457. guest = Guest()
  458. guest.uuid = uuid
  459. guest.get_by('uuid')
  460. disk = Disk()
  461. disk.uuid = disk_uuid
  462. disk.get_by('uuid')
  463. config = Config()
  464. config.id = 1
  465. config.get()
  466. ret = dict()
  467. ret['state'] = ji.Common.exchange_state(20000)
  468. # 判断欲挂载的磁盘是否空闲
  469. if disk.guest_uuid.__len__() > 0 or disk.state != DiskState.idle.value:
  470. ret['state'] = ji.Common.exchange_state(41258)
  471. return ret
  472. # 判断 Guest 是否处于可用状态
  473. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  474. ret['state'] = ji.Common.exchange_state(41259)
  475. return ret
  476. # 判断 Guest 与 磁盘是否在同一宿主机上
  477. if config.storage_mode in [status.StorageMode.local.value, status.StorageMode.shared_mount.value]:
  478. if guest.node_id != disk.node_id:
  479. ret['state'] = ji.Common.exchange_state(41260)
  480. return ret
  481. # 通过检测未被使用的序列,来确定当前磁盘在目标 Guest 身上的序列
  482. disk.guest_uuid = guest.uuid
  483. disks, count = disk.get_by_filter(filter_str='guest_uuid:in:' + guest.uuid)
  484. already_used_sequence = list()
  485. for _disk in disks:
  486. already_used_sequence.append(_disk['sequence'])
  487. for sequence in range(0, dev_table.__len__()):
  488. if sequence not in already_used_sequence:
  489. disk.sequence = sequence
  490. break
  491. disk.state = DiskState.mounting.value
  492. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  493. message = {
  494. '_object': 'guest',
  495. 'action': 'attach_disk',
  496. 'uuid': uuid,
  497. 'node_id': guest.node_id,
  498. 'xml': guest_xml.get_disk(),
  499. 'passback_parameters': {'disk_uuid': disk.uuid, 'sequence': disk.sequence},
  500. 'disks': [disk.__dict__]
  501. }
  502. Utils.emit_instruction(message=json.dumps(message))
  503. disk.update()
  504. return ret
  505. except ji.PreviewingError, e:
  506. return json.loads(e.message)
  507. @Utils.dumps2response
  508. def r_detach_disk(disk_uuid):
  509. args_rules = [
  510. Rules.DISK_UUID.value
  511. ]
  512. try:
  513. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  514. disk = Disk()
  515. disk.uuid = disk_uuid
  516. disk.get_by('uuid')
  517. ret = dict()
  518. ret['state'] = ji.Common.exchange_state(20000)
  519. if disk.state != DiskState.mounted.value or disk.sequence == 0:
  520. # 表示未被任何实例使用,已被分离
  521. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  522. # TODO: 系统盘单独范围其它状态
  523. return ret
  524. guest = Guest()
  525. guest.uuid = disk.guest_uuid
  526. guest.get_by('uuid')
  527. # 判断 Guest 是否处于可用状态
  528. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  529. ret['state'] = ji.Common.exchange_state(41259)
  530. return ret
  531. config = Config()
  532. config.id = 1
  533. config.get()
  534. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  535. message = {
  536. '_object': 'guest',
  537. 'action': 'detach_disk',
  538. 'uuid': disk.guest_uuid,
  539. 'node_id': guest.node_id,
  540. 'xml': guest_xml.get_disk(),
  541. 'passback_parameters': {'disk_uuid': disk.uuid}
  542. }
  543. Utils.emit_instruction(message=json.dumps(message))
  544. disk.state = DiskState.unloading.value
  545. disk.update()
  546. return ret
  547. except ji.PreviewingError, e:
  548. return json.loads(e.message)
  549. @Utils.dumps2response
  550. def r_migrate(uuids, destination_host):
  551. args_rules = [
  552. Rules.UUIDS.value,
  553. Rules.DESTINATION_HOST.value
  554. ]
  555. try:
  556. ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
  557. ret = dict()
  558. ret['state'] = ji.Common.exchange_state(20000)
  559. # 取全部活着的 hosts
  560. available_hosts = Host.get_available_hosts(nonrandom=None)
  561. if available_hosts.__len__() == 0:
  562. ret['state'] = ji.Common.exchange_state(50351)
  563. return ret
  564. available_hosts_mapping_by_node_id = dict()
  565. for host in available_hosts:
  566. if host['node_id'] not in available_hosts_mapping_by_node_id:
  567. available_hosts_mapping_by_node_id[host['node_id']] = host
  568. guest = Guest()
  569. for uuid in uuids.split(','):
  570. guest.uuid = uuid
  571. guest.get_by('uuid')
  572. config = Config()
  573. config.id = 1
  574. config.get()
  575. for uuid in uuids.split(','):
  576. guest.uuid = uuid
  577. guest.get_by('uuid')
  578. # 忽略宕机计算节点 上面的 虚拟机 迁移请求
  579. # 忽略目标计算节点 等于 当前所在 计算节点 的虚拟机 迁移请求
  580. if guest.node_id not in available_hosts_mapping_by_node_id or \
  581. available_hosts_mapping_by_node_id[guest.node_id]['hostname'] == destination_host:
  582. continue
  583. message = {
  584. '_object': 'guest',
  585. 'action': 'migrate',
  586. 'uuid': uuid,
  587. 'node_id': guest.node_id,
  588. 'storage_mode': config.storage_mode,
  589. 'duri': 'qemu+ssh://' + destination_host + '/system'
  590. }
  591. Utils.emit_instruction(message=json.dumps(message))
  592. return ret
  593. except ji.PreviewingError, e:
  594. return json.loads(e.message)
  595. @Utils.dumps2response
  596. def r_get(uuids):
  597. ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  598. if '200' != ret['state']['code']:
  599. return ret
  600. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', uuids]))
  601. guest_uuid_ssh_key_id_mapping = dict()
  602. ssh_keys_id = list()
  603. for row in rows:
  604. if row['ssh_key_id'] not in ssh_keys_id:
  605. ssh_keys_id.append(row['ssh_key_id'].__str__())
  606. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  607. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  608. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  609. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  610. ssh_key_id_mapping = dict()
  611. for row in rows:
  612. row['url'] = url_for('v_ssh_keys.show')
  613. ssh_key_id_mapping[row['id']] = row
  614. if -1 == uuids.find(','):
  615. if 'ssh_keys' not in ret['data']:
  616. ret['data']['ssh_keys'] = list()
  617. if ret['data']['uuid'] in guest_uuid_ssh_key_id_mapping:
  618. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data']['uuid']]:
  619. if ssh_key_id not in ssh_key_id_mapping:
  620. continue
  621. ret['data']['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  622. else:
  623. for i, guest in enumerate(ret['data']):
  624. if 'ssh_keys' not in ret['data'][i]:
  625. ret['data'][i]['ssh_keys'] = list()
  626. if ret['data'][i]['uuid'] in guest_uuid_ssh_key_id_mapping:
  627. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data'][i]['uuid']]:
  628. if ssh_key_id not in ssh_key_id_mapping:
  629. continue
  630. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  631. return ret
  632. @Utils.dumps2response
  633. def r_get_by_filter():
  634. ret = guest_base.get_by_filter()
  635. uuids = list()
  636. for guest in ret['data']:
  637. uuids.append(guest['uuid'])
  638. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  639. guest_uuid_ssh_key_id_mapping = dict()
  640. ssh_keys_id = list()
  641. for row in rows:
  642. if row['ssh_key_id'] not in ssh_keys_id:
  643. ssh_keys_id.append(row['ssh_key_id'].__str__())
  644. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  645. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  646. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  647. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  648. ssh_key_id_mapping = dict()
  649. for row in rows:
  650. row['url'] = url_for('v_ssh_keys.show')
  651. ssh_key_id_mapping[row['id']] = row
  652. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  653. snapshots_guest_uuid_mapping = dict()
  654. for row in rows:
  655. guest_uuid = row['guest_uuid']
  656. if guest_uuid not in snapshots_guest_uuid_mapping:
  657. snapshots_guest_uuid_mapping[guest_uuid] = list()
  658. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  659. for i, guest in enumerate(ret['data']):
  660. guest_uuid = ret['data'][i]['uuid']
  661. if 'ssh_keys' not in ret['data'][i]:
  662. ret['data'][i]['ssh_keys'] = list()
  663. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  664. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  665. if ssh_key_id not in ssh_key_id_mapping:
  666. continue
  667. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  668. if 'snapshot' not in ret['data'][i]:
  669. ret['data'][i]['snapshot'] = {
  670. 'creatable': True,
  671. 'mapping': list()
  672. }
  673. if guest_uuid in snapshots_guest_uuid_mapping:
  674. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  675. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  676. if snapshot['progress'] == 100:
  677. continue
  678. else:
  679. ret['data'][i]['snapshot']['creatable'] = False
  680. return ret
  681. @Utils.dumps2response
  682. def r_content_search():
  683. ret = guest_base.content_search()
  684. uuids = list()
  685. for guest in ret['data']:
  686. uuids.append(guest['uuid'])
  687. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  688. guest_uuid_ssh_key_id_mapping = dict()
  689. ssh_keys_id = list()
  690. for row in rows:
  691. if row['ssh_key_id'] not in ssh_keys_id:
  692. ssh_keys_id.append(row['ssh_key_id'].__str__())
  693. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  694. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  695. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  696. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  697. ssh_key_id_mapping = dict()
  698. for row in rows:
  699. row['url'] = url_for('v_ssh_keys.show')
  700. ssh_key_id_mapping[row['id']] = row
  701. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  702. snapshots_guest_uuid_mapping = dict()
  703. for row in rows:
  704. guest_uuid = row['guest_uuid']
  705. if guest_uuid not in snapshots_guest_uuid_mapping:
  706. snapshots_guest_uuid_mapping[guest_uuid] = list()
  707. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  708. for i, guest in enumerate(ret['data']):
  709. guest_uuid = ret['data'][i]['uuid']
  710. if 'ssh_keys' not in ret['data'][i]:
  711. ret['data'][i]['ssh_keys'] = list()
  712. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  713. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  714. if ssh_key_id not in ssh_key_id_mapping:
  715. continue
  716. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  717. if 'snapshot' not in ret['data'][i]:
  718. ret['data'][i]['snapshot'] = {
  719. 'creatable': True,
  720. 'mapping': list()
  721. }
  722. if guest_uuid in snapshots_guest_uuid_mapping:
  723. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  724. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  725. if snapshot['progress'] == 100:
  726. continue
  727. else:
  728. ret['data'][i]['snapshot']['creatable'] = False
  729. return ret
  730. @Utils.dumps2response
  731. def r_distribute_count():
  732. from models import Guest
  733. rows, count = Guest.get_all()
  734. ret = dict()
  735. ret['state'] = ji.Common.exchange_state(20000)
  736. ret['data'] = {
  737. 'os_template_image_id': dict(),
  738. 'status': dict(),
  739. 'node_id': dict(),
  740. 'cpu_memory': dict(),
  741. 'cpu': 0,
  742. 'memory': 0,
  743. 'guests': rows.__len__()
  744. }
  745. for guest in rows:
  746. if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
  747. ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
  748. if guest['status'] not in ret['data']['status']:
  749. ret['data']['status'][guest['status']] = 0
  750. if guest['node_id'] not in ret['data']['node_id']:
  751. ret['data']['node_id'][guest['node_id']] = 0
  752. cpu_memory = '_'.join([str(guest['cpu']), str(guest['memory'])])
  753. if cpu_memory not in ret['data']['cpu_memory']:
  754. ret['data']['cpu_memory'][cpu_memory] = 0
  755. ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
  756. ret['data']['status'][guest['status']] += 1
  757. ret['data']['node_id'][guest['node_id']] += 1
  758. ret['data']['cpu_memory'][cpu_memory] += 1
  759. ret['data']['cpu'] += guest['cpu']
  760. ret['data']['memory'] += guest['memory']
  761. return ret
  762. @Utils.dumps2response
  763. def r_update(uuid):
  764. args_rules = [
  765. Rules.UUID.value
  766. ]
  767. if 'remark' in request.json:
  768. args_rules.append(
  769. Rules.REMARK.value,
  770. )
  771. if args_rules.__len__() < 2:
  772. ret = dict()
  773. ret['state'] = ji.Common.exchange_state(20000)
  774. return ret
  775. request.json['uuid'] = uuid
  776. try:
  777. ji.Check.previewing(args_rules, request.json)
  778. guest = Guest()
  779. guest.uuid = uuid
  780. guest.get_by('uuid')
  781. guest.remark = request.json.get('remark', guest.label)
  782. guest.update()
  783. guest.get()
  784. ret = dict()
  785. ret['state'] = ji.Common.exchange_state(20000)
  786. ret['data'] = guest.__dict__
  787. return ret
  788. except ji.PreviewingError, e:
  789. return json.loads(e.message)
  790. @Utils.dumps2response
  791. def r_reset_password(uuids, password):
  792. args_rules = [
  793. Rules.UUIDS.value,
  794. Rules.PASSWORD.value
  795. ]
  796. try:
  797. ji.Check.previewing(args_rules, {'uuids': uuids, 'password': password})
  798. guest = Guest()
  799. os_template_image = OSTemplateImage()
  800. os_template_profile = OSTemplateProfile()
  801. # 检测所指定的 UUDIs 实例都存在
  802. for uuid in uuids.split(','):
  803. guest.uuid = uuid
  804. guest.get_by('uuid')
  805. for uuid in uuids.split(','):
  806. guest.uuid = uuid
  807. guest.get_by('uuid')
  808. os_template_image.id = guest.os_template_image_id
  809. os_template_image.get()
  810. os_template_profile.id = os_template_image.os_template_profile_id
  811. os_template_profile.get()
  812. user = 'root'
  813. if os_template_profile.os_type == 'windows':
  814. user = 'administrator'
  815. # guest.password 由 guest 事件处理机更新。参见 @models/event_processory.py:189 附近。
  816. message = {
  817. '_object': 'guest',
  818. 'action': 'reset_password',
  819. 'uuid': guest.uuid,
  820. 'node_id': guest.node_id,
  821. 'os_type': os_template_profile.os_type,
  822. 'user': user,
  823. 'password': password,
  824. 'passback_parameters': {'password': password}
  825. }
  826. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  827. ret = dict()
  828. ret['state'] = ji.Common.exchange_state(20000)
  829. return ret
  830. except ji.PreviewingError, e:
  831. return json.loads(e.message)
  832. @Utils.dumps2response
  833. def r_allocate_bandwidth(uuids, bandwidth, bandwidth_unit):
  834. args_rules = [
  835. Rules.UUIDS.value,
  836. Rules.BANDWIDTH_IN_URL.value,
  837. Rules.BANDWIDTH_UNIT.value,
  838. ]
  839. try:
  840. ji.Check.previewing(args_rules, {'uuids': uuids, 'bandwidth': bandwidth, 'bandwidth_unit': bandwidth_unit})
  841. ret = dict()
  842. ret['state'] = ji.Common.exchange_state(20000)
  843. bandwidth = int(bandwidth)
  844. if bandwidth_unit == 'k':
  845. bandwidth = bandwidth * 1000
  846. elif bandwidth_unit == 'm':
  847. bandwidth = bandwidth * 1000 ** 2
  848. elif bandwidth_unit == 'g':
  849. bandwidth = bandwidth * 1000 ** 3
  850. else:
  851. ret['state'] = ji.Common.exchange_state(41203)
  852. return ret
  853. # http://man7.org/linux/man-pages/man8/tc.8.html
  854. # 如果带宽大于 tc 所控最大速率,则置其为无限带宽
  855. # 34359738360 等于 tc 最大可控字节速率,换算出的比特位
  856. if bandwidth > 34359738360:
  857. bandwidth = 0
  858. guest = Guest()
  859. # 检测所指定的 UUDIs 实例都存在
  860. for uuid in uuids.split(','):
  861. guest.uuid = uuid
  862. guest.get_by('uuid')
  863. for uuid in uuids.split(','):
  864. guest.uuid = uuid
  865. guest.get_by('uuid')
  866. guest.bandwidth = bandwidth
  867. message = {
  868. '_object': 'guest',
  869. 'action': 'allocate_bandwidth',
  870. 'uuid': guest.uuid,
  871. 'node_id': guest.node_id,
  872. 'bandwidth': guest.bandwidth,
  873. 'passback_parameters': {'bandwidth': guest.bandwidth}
  874. }
  875. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  876. return ret
  877. except ji.PreviewingError, e:
  878. return json.loads(e.message)
  879. @Utils.dumps2response
  880. def r_adjust_ability(uuids, cpu, memory):
  881. args_rules = [
  882. Rules.UUIDS.value,
  883. Rules.CPU.value,
  884. Rules.MEMORY.value,
  885. ]
  886. try:
  887. ret = dict()
  888. ret['state'] = ji.Common.exchange_state(20000)
  889. cpu = int(cpu)
  890. memory = int(memory)
  891. ji.Check.previewing(args_rules, {'uuids': uuids, 'cpu': cpu, 'memory': memory})
  892. not_ready_yet_of_guests = list()
  893. guest = Guest()
  894. # 检测所指定的 UUDIs 实例都存在。且状态都为可以操作状态(即关闭状态)。
  895. for uuid in uuids.split(','):
  896. guest.uuid = uuid
  897. guest.get_by('uuid')
  898. if guest.status != status.GuestState.shutoff.value:
  899. not_ready_yet_of_guests.append(guest.__dict__)
  900. if not_ready_yet_of_guests.__len__() > 0:
  901. ret['state'] = ji.Common.exchange_state(41261)
  902. ret['data'] = not_ready_yet_of_guests
  903. return ret
  904. for uuid in uuids.split(','):
  905. guest.uuid = uuid
  906. guest.get_by('uuid')
  907. guest.cpu = cpu
  908. guest.memory = memory
  909. message = {
  910. '_object': 'guest',
  911. 'action': 'adjust_ability',
  912. 'uuid': guest.uuid,
  913. 'node_id': guest.node_id,
  914. 'cpu': guest.cpu,
  915. 'memory': guest.memory,
  916. 'passback_parameters': {'cpu': guest.cpu, 'memory': guest.memory}
  917. }
  918. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  919. return ret
  920. except ji.PreviewingError, e:
  921. return json.loads(e.message)
  922. @Utils.dumps2response
  923. def r_refresh_guest_state():
  924. try:
  925. ret = dict()
  926. ret['state'] = ji.Common.exchange_state(20000)
  927. # 取全部活着的 hosts
  928. available_hosts = Host.get_available_hosts(nonrandom=None)
  929. if available_hosts.__len__() == 0:
  930. ret['state'] = ji.Common.exchange_state(50351)
  931. return ret
  932. for host in available_hosts:
  933. message = {
  934. '_object': 'global',
  935. 'action': 'refresh_guest_state',
  936. 'node_id': host['node_id']
  937. }
  938. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  939. except ji.PreviewingError, e:
  940. return json.loads(e.message)