guest.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  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. quantity = request.json.get('quantity')
  123. while quantity:
  124. quantity -= 1
  125. guest = Guest()
  126. guest.uuid = uuid4().__str__()
  127. guest.cpu = request.json.get('cpu')
  128. # 虚拟机内存单位,模板生成方法中已置其为GiB
  129. guest.memory = request.json.get('memory')
  130. guest.bandwidth = bandwidth
  131. guest.os_template_image_id = request.json.get('os_template_image_id')
  132. guest.label = ji.Common.generate_random_code(length=8)
  133. guest.remark = request.json.get('remark', '')
  134. guest.password = request.json.get('password')
  135. if guest.password is None or guest.password.__len__() < 1:
  136. guest.password = ji.Common.generate_random_code(length=16)
  137. guest.ip = db.r.spop(app.config['ip_available_set'])
  138. db.r.sadd(app.config['ip_used_set'], guest.ip)
  139. guest.network = config.vm_network
  140. guest.manage_network = config.vm_manage_network
  141. guest.vnc_port = db.r.spop(app.config['vnc_port_available_set'])
  142. db.r.sadd(app.config['vnc_port_used_set'], guest.vnc_port)
  143. guest.vnc_password = ji.Common.generate_random_code(length=16)
  144. disk = Disk()
  145. disk.uuid = guest.uuid
  146. disk.remark = guest.label.__str__() + '_SystemImage'
  147. disk.format = 'qcow2'
  148. disk.sequence = 0
  149. disk.size = 0
  150. disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
  151. disk.guest_uuid = ''
  152. # disk.node_id 由 guest 事件处理机更新。涉及迁移时,其所属 node_id 会变更。参见 @models/event_processory.py:111 附近。
  153. disk.node_id = 0
  154. disk.quota(config=config)
  155. disk.create()
  156. if node_id is None:
  157. # 在可用计算节点中平均分配任务
  158. chosen_host = available_hosts[quantity % available_hosts.__len__()]
  159. else:
  160. chosen_host = available_hosts_mapping_by_node_id[node_id]
  161. guest.node_id = chosen_host['node_id']
  162. guest_xml = GuestXML(host=chosen_host, guest=guest, disk=disk, config=config,
  163. os_type=os_template_profile.os_type)
  164. guest.xml = guest_xml.get_domain()
  165. guest.node_id = int(guest.node_id)
  166. guest.create()
  167. ssh_key_guest_mapping.guest_uuid = guest.uuid
  168. if ssh_keys_id.__len__() > 0:
  169. for ssh_key_id in ssh_keys_id:
  170. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  171. ssh_key_guest_mapping.create()
  172. # 替换占位符为有效内容
  173. _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
  174. for k, v in enumerate(_os_template_initialize_operates):
  175. _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
  176. replace('{HOSTNAME}', guest.label). \
  177. replace('{PASSWORD}', guest.password). \
  178. replace('{NETMASK}', config.netmask).\
  179. replace('{GATEWAY}', config.gateway).\
  180. replace('{DNS1}', config.dns1).\
  181. replace('{DNS2}', config.dns2). \
  182. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  183. _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
  184. replace('{HOSTNAME}', guest.label). \
  185. replace('{PASSWORD}', guest.password). \
  186. replace('{NETMASK}', config.netmask). \
  187. replace('{GATEWAY}', config.gateway). \
  188. replace('{DNS1}', config.dns1). \
  189. replace('{DNS2}', config.dns2). \
  190. replace('{SSH-KEY}', '\n'.join(ssh_keys))
  191. message = {
  192. '_object': 'guest',
  193. 'action': 'create',
  194. 'uuid': guest.uuid,
  195. 'storage_mode': config.storage_mode,
  196. 'dfs_volume': config.dfs_volume,
  197. 'node_id': guest.node_id,
  198. 'name': guest.label,
  199. 'template_path': os_template_image.path,
  200. 'os_type': os_template_profile.os_type,
  201. 'disks': [disk.__dict__],
  202. 'xml': guest_xml.get_domain(),
  203. 'os_template_initialize_operates': _os_template_initialize_operates,
  204. 'passback_parameters': {}
  205. }
  206. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  207. return ret
  208. except ji.PreviewingError, e:
  209. return json.loads(e.message)
  210. @Utils.dumps2response
  211. def r_reboot(uuids):
  212. args_rules = [
  213. Rules.UUIDS.value
  214. ]
  215. try:
  216. ji.Check.previewing(args_rules, {'uuids': uuids})
  217. guest = Guest()
  218. for uuid in uuids.split(','):
  219. guest.uuid = uuid
  220. guest.get_by('uuid')
  221. for uuid in uuids.split(','):
  222. guest.uuid = uuid
  223. guest.get_by('uuid')
  224. message = {
  225. '_object': 'guest',
  226. 'action': 'reboot',
  227. 'uuid': uuid,
  228. 'node_id': guest.node_id
  229. }
  230. Utils.emit_instruction(message=json.dumps(message))
  231. ret = dict()
  232. ret['state'] = ji.Common.exchange_state(20000)
  233. return ret
  234. except ji.PreviewingError, e:
  235. return json.loads(e.message)
  236. @Utils.dumps2response
  237. def r_force_reboot(uuids):
  238. args_rules = [
  239. Rules.UUIDS.value
  240. ]
  241. try:
  242. ji.Check.previewing(args_rules, {'uuids': uuids})
  243. guest = Guest()
  244. for uuid in uuids.split(','):
  245. guest.uuid = uuid
  246. guest.get_by('uuid')
  247. for uuid in uuids.split(','):
  248. guest.uuid = uuid
  249. guest.get_by('uuid')
  250. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  251. message = {
  252. '_object': 'guest',
  253. 'action': 'force_reboot',
  254. 'uuid': uuid,
  255. 'node_id': guest.node_id,
  256. 'disks': disks
  257. }
  258. Utils.emit_instruction(message=json.dumps(message))
  259. ret = dict()
  260. ret['state'] = ji.Common.exchange_state(20000)
  261. return ret
  262. except ji.PreviewingError, e:
  263. return json.loads(e.message)
  264. @Utils.dumps2response
  265. def r_shutdown(uuids):
  266. args_rules = [
  267. Rules.UUIDS.value
  268. ]
  269. try:
  270. ji.Check.previewing(args_rules, {'uuids': uuids})
  271. guest = Guest()
  272. for uuid in uuids.split(','):
  273. guest.uuid = uuid
  274. guest.get_by('uuid')
  275. for uuid in uuids.split(','):
  276. guest.uuid = uuid
  277. guest.get_by('uuid')
  278. message = {
  279. '_object': 'guest',
  280. 'action': 'shutdown',
  281. 'uuid': uuid,
  282. 'node_id': guest.node_id
  283. }
  284. Utils.emit_instruction(message=json.dumps(message))
  285. ret = dict()
  286. ret['state'] = ji.Common.exchange_state(20000)
  287. return ret
  288. except ji.PreviewingError, e:
  289. return json.loads(e.message)
  290. @Utils.dumps2response
  291. def r_force_shutdown(uuids):
  292. args_rules = [
  293. Rules.UUIDS.value
  294. ]
  295. try:
  296. ji.Check.previewing(args_rules, {'uuids': uuids})
  297. guest = Guest()
  298. for uuid in uuids.split(','):
  299. guest.uuid = uuid
  300. guest.get_by('uuid')
  301. for uuid in uuids.split(','):
  302. guest.uuid = uuid
  303. guest.get_by('uuid')
  304. message = {
  305. '_object': 'guest',
  306. 'action': 'force_shutdown',
  307. 'uuid': uuid,
  308. 'node_id': guest.node_id
  309. }
  310. Utils.emit_instruction(message=json.dumps(message))
  311. ret = dict()
  312. ret['state'] = ji.Common.exchange_state(20000)
  313. return ret
  314. except ji.PreviewingError, e:
  315. return json.loads(e.message)
  316. @Utils.dumps2response
  317. def r_boot(uuids):
  318. # TODO: 做好关系依赖判断,比如boot不可以对suspend的实例操作。
  319. args_rules = [
  320. Rules.UUIDS.value
  321. ]
  322. try:
  323. ji.Check.previewing(args_rules, {'uuids': uuids})
  324. guest = Guest()
  325. for uuid in uuids.split(','):
  326. guest.uuid = uuid
  327. guest.get_by('uuid')
  328. config = Config()
  329. config.id = 1
  330. config.get()
  331. for uuid in uuids.split(','):
  332. guest.uuid = uuid
  333. guest.get_by('uuid')
  334. disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  335. message = {
  336. '_object': 'guest',
  337. 'action': 'boot',
  338. 'uuid': uuid,
  339. 'node_id': guest.node_id,
  340. 'passback_parameters': {},
  341. 'disks': disks
  342. }
  343. Utils.emit_instruction(message=json.dumps(message))
  344. ret = dict()
  345. ret['state'] = ji.Common.exchange_state(20000)
  346. return ret
  347. except ji.PreviewingError, e:
  348. return json.loads(e.message)
  349. @Utils.dumps2response
  350. def r_suspend(uuids):
  351. args_rules = [
  352. Rules.UUIDS.value
  353. ]
  354. try:
  355. ji.Check.previewing(args_rules, {'uuids': uuids})
  356. guest = Guest()
  357. for uuid in uuids.split(','):
  358. guest.uuid = uuid
  359. guest.get_by('uuid')
  360. for uuid in uuids.split(','):
  361. guest.uuid = uuid
  362. guest.get_by('uuid')
  363. message = {
  364. '_object': 'guest',
  365. 'action': 'suspend',
  366. 'uuid': uuid,
  367. 'node_id': guest.node_id
  368. }
  369. Utils.emit_instruction(message=json.dumps(message))
  370. ret = dict()
  371. ret['state'] = ji.Common.exchange_state(20000)
  372. return ret
  373. except ji.PreviewingError, e:
  374. return json.loads(e.message)
  375. @Utils.dumps2response
  376. def r_resume(uuids):
  377. args_rules = [
  378. Rules.UUIDS.value
  379. ]
  380. try:
  381. ji.Check.previewing(args_rules, {'uuids': uuids})
  382. guest = Guest()
  383. for uuid in uuids.split(','):
  384. guest.uuid = uuid
  385. guest.get_by('uuid')
  386. for uuid in uuids.split(','):
  387. guest.uuid = uuid
  388. guest.get_by('uuid')
  389. message = {
  390. '_object': 'guest',
  391. 'action': 'resume',
  392. 'uuid': uuid,
  393. 'node_id': guest.node_id
  394. }
  395. Utils.emit_instruction(message=json.dumps(message))
  396. ret = dict()
  397. ret['state'] = ji.Common.exchange_state(20000)
  398. return ret
  399. except ji.PreviewingError, e:
  400. return json.loads(e.message)
  401. @Utils.dumps2response
  402. def r_delete(uuids):
  403. args_rules = [
  404. Rules.UUIDS.value
  405. ]
  406. # TODO: 加入是否删除使用的数据磁盘开关,如果为True,则顺便删除使用的磁盘。否则解除该磁盘被使用的状态。
  407. try:
  408. ji.Check.previewing(args_rules, {'uuids': uuids})
  409. guest = Guest()
  410. # 检测所指定的 UUDIs 实例都存在
  411. for uuid in uuids.split(','):
  412. guest.uuid = uuid
  413. guest.get_by('uuid')
  414. config = Config()
  415. config.id = 1
  416. config.get()
  417. # 执行删除操作
  418. for uuid in uuids.split(','):
  419. guest.uuid = uuid
  420. guest.get_by('uuid')
  421. message = {
  422. '_object': 'guest',
  423. 'action': 'delete',
  424. 'uuid': uuid,
  425. 'storage_mode': config.storage_mode,
  426. 'dfs_volume': config.dfs_volume,
  427. 'node_id': guest.node_id
  428. }
  429. Utils.emit_instruction(message=json.dumps(message))
  430. # 删除创建失败的 Guest
  431. if guest.status == status.GuestState.dirty.value:
  432. disk = Disk()
  433. disk.uuid = guest.uuid
  434. disk.get_by('uuid')
  435. if disk.state == status.DiskState.pending.value:
  436. disk.delete()
  437. guest.delete()
  438. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
  439. ret = dict()
  440. ret['state'] = ji.Common.exchange_state(20000)
  441. return ret
  442. except ji.PreviewingError, e:
  443. return json.loads(e.message)
  444. @Utils.dumps2response
  445. def r_attach_disk(uuid, disk_uuid):
  446. args_rules = [
  447. Rules.UUID.value,
  448. Rules.DISK_UUID.value
  449. ]
  450. try:
  451. ji.Check.previewing(args_rules, {'uuid': uuid, 'disk_uuid': disk_uuid})
  452. guest = Guest()
  453. guest.uuid = uuid
  454. guest.get_by('uuid')
  455. disk = Disk()
  456. disk.uuid = disk_uuid
  457. disk.get_by('uuid')
  458. config = Config()
  459. config.id = 1
  460. config.get()
  461. ret = dict()
  462. ret['state'] = ji.Common.exchange_state(20000)
  463. # 判断欲挂载的磁盘是否空闲
  464. if disk.guest_uuid.__len__() > 0 or disk.state != DiskState.idle.value:
  465. ret['state'] = ji.Common.exchange_state(41258)
  466. return ret
  467. # 判断 Guest 是否处于可用状态
  468. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  469. ret['state'] = ji.Common.exchange_state(41259)
  470. return ret
  471. # 判断 Guest 与 磁盘是否在同一宿主机上
  472. if config.storage_mode in [status.StorageMode.local.value, status.StorageMode.shared_mount.value]:
  473. if guest.node_id != disk.node_id:
  474. ret['state'] = ji.Common.exchange_state(41260)
  475. return ret
  476. # 通过检测未被使用的序列,来确定当前磁盘在目标 Guest 身上的序列
  477. disk.guest_uuid = guest.uuid
  478. disks, count = disk.get_by_filter(filter_str='guest_uuid:in:' + guest.uuid)
  479. already_used_sequence = list()
  480. for _disk in disks:
  481. already_used_sequence.append(_disk['sequence'])
  482. for sequence in range(0, dev_table.__len__()):
  483. if sequence not in already_used_sequence:
  484. disk.sequence = sequence
  485. break
  486. disk.state = DiskState.mounting.value
  487. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  488. message = {
  489. '_object': 'guest',
  490. 'action': 'attach_disk',
  491. 'uuid': uuid,
  492. 'node_id': guest.node_id,
  493. 'xml': guest_xml.get_disk(),
  494. 'passback_parameters': {'disk_uuid': disk.uuid, 'sequence': disk.sequence},
  495. 'disks': [disk.__dict__]
  496. }
  497. Utils.emit_instruction(message=json.dumps(message))
  498. disk.update()
  499. return ret
  500. except ji.PreviewingError, e:
  501. return json.loads(e.message)
  502. @Utils.dumps2response
  503. def r_detach_disk(disk_uuid):
  504. args_rules = [
  505. Rules.DISK_UUID.value
  506. ]
  507. try:
  508. ji.Check.previewing(args_rules, {'disk_uuid': disk_uuid})
  509. disk = Disk()
  510. disk.uuid = disk_uuid
  511. disk.get_by('uuid')
  512. ret = dict()
  513. ret['state'] = ji.Common.exchange_state(20000)
  514. if disk.state != DiskState.mounted.value or disk.sequence == 0:
  515. # 表示未被任何实例使用,已被分离
  516. # 序列为 0 的表示实例系统盘,系统盘不可以被分离
  517. # TODO: 系统盘单独范围其它状态
  518. return ret
  519. guest = Guest()
  520. guest.uuid = disk.guest_uuid
  521. guest.get_by('uuid')
  522. # 判断 Guest 是否处于可用状态
  523. if guest.status in (status.GuestState.no_state.value, status.GuestState.dirty.value):
  524. ret['state'] = ji.Common.exchange_state(41259)
  525. return ret
  526. config = Config()
  527. config.id = 1
  528. config.get()
  529. guest_xml = GuestXML(guest=guest, disk=disk, config=config)
  530. message = {
  531. '_object': 'guest',
  532. 'action': 'detach_disk',
  533. 'uuid': disk.guest_uuid,
  534. 'node_id': guest.node_id,
  535. 'xml': guest_xml.get_disk(),
  536. 'passback_parameters': {'disk_uuid': disk.uuid}
  537. }
  538. Utils.emit_instruction(message=json.dumps(message))
  539. disk.state = DiskState.unloading.value
  540. disk.update()
  541. return ret
  542. except ji.PreviewingError, e:
  543. return json.loads(e.message)
  544. @Utils.dumps2response
  545. def r_migrate(uuids, destination_host):
  546. args_rules = [
  547. Rules.UUIDS.value,
  548. Rules.DESTINATION_HOST.value
  549. ]
  550. try:
  551. ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
  552. guest = Guest()
  553. for uuid in uuids.split(','):
  554. guest.uuid = uuid
  555. guest.get_by('uuid')
  556. config = Config()
  557. config.id = 1
  558. config.get()
  559. for uuid in uuids.split(','):
  560. guest.uuid = uuid
  561. guest.get_by('uuid')
  562. message = {
  563. '_object': 'guest',
  564. 'action': 'migrate',
  565. 'uuid': uuid,
  566. 'node_id': guest.node_id,
  567. 'storage_mode': config.storage_mode,
  568. 'duri': 'qemu+ssh://' + destination_host + '/system'
  569. }
  570. Utils.emit_instruction(message=json.dumps(message))
  571. ret = dict()
  572. ret['state'] = ji.Common.exchange_state(20000)
  573. return ret
  574. except ji.PreviewingError, e:
  575. return json.loads(e.message)
  576. @Utils.dumps2response
  577. def r_get(uuids):
  578. ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
  579. if '200' != ret['state']['code']:
  580. return ret
  581. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', uuids]))
  582. guest_uuid_ssh_key_id_mapping = dict()
  583. ssh_keys_id = list()
  584. for row in rows:
  585. if row['ssh_key_id'] not in ssh_keys_id:
  586. ssh_keys_id.append(row['ssh_key_id'].__str__())
  587. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  588. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  589. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  590. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  591. ssh_key_id_mapping = dict()
  592. for row in rows:
  593. row['url'] = url_for('v_ssh_keys.show')
  594. ssh_key_id_mapping[row['id']] = row
  595. if -1 == uuids.find(','):
  596. if 'ssh_keys' not in ret['data']:
  597. ret['data']['ssh_keys'] = list()
  598. if ret['data']['uuid'] in guest_uuid_ssh_key_id_mapping:
  599. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data']['uuid']]:
  600. if ssh_key_id not in ssh_key_id_mapping:
  601. continue
  602. ret['data']['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  603. else:
  604. for i, guest in enumerate(ret['data']):
  605. if 'ssh_keys' not in ret['data'][i]:
  606. ret['data'][i]['ssh_keys'] = list()
  607. if ret['data'][i]['uuid'] in guest_uuid_ssh_key_id_mapping:
  608. for ssh_key_id in guest_uuid_ssh_key_id_mapping[ret['data'][i]['uuid']]:
  609. if ssh_key_id not in ssh_key_id_mapping:
  610. continue
  611. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  612. return ret
  613. @Utils.dumps2response
  614. def r_get_by_filter():
  615. ret = guest_base.get_by_filter()
  616. uuids = list()
  617. for guest in ret['data']:
  618. uuids.append(guest['uuid'])
  619. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  620. guest_uuid_ssh_key_id_mapping = dict()
  621. ssh_keys_id = list()
  622. for row in rows:
  623. if row['ssh_key_id'] not in ssh_keys_id:
  624. ssh_keys_id.append(row['ssh_key_id'].__str__())
  625. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  626. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  627. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  628. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  629. ssh_key_id_mapping = dict()
  630. for row in rows:
  631. row['url'] = url_for('v_ssh_keys.show')
  632. ssh_key_id_mapping[row['id']] = row
  633. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  634. snapshots_guest_uuid_mapping = dict()
  635. for row in rows:
  636. guest_uuid = row['guest_uuid']
  637. if guest_uuid not in snapshots_guest_uuid_mapping:
  638. snapshots_guest_uuid_mapping[guest_uuid] = list()
  639. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  640. for i, guest in enumerate(ret['data']):
  641. guest_uuid = ret['data'][i]['uuid']
  642. if 'ssh_keys' not in ret['data'][i]:
  643. ret['data'][i]['ssh_keys'] = list()
  644. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  645. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  646. if ssh_key_id not in ssh_key_id_mapping:
  647. continue
  648. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  649. if 'snapshot' not in ret['data'][i]:
  650. ret['data'][i]['snapshot'] = {
  651. 'creatable': True,
  652. 'mapping': list()
  653. }
  654. if guest_uuid in snapshots_guest_uuid_mapping:
  655. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  656. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  657. if snapshot['progress'] == 100:
  658. continue
  659. else:
  660. ret['data'][i]['snapshot']['creatable'] = False
  661. return ret
  662. @Utils.dumps2response
  663. def r_content_search():
  664. ret = guest_base.content_search()
  665. uuids = list()
  666. for guest in ret['data']:
  667. uuids.append(guest['uuid'])
  668. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  669. guest_uuid_ssh_key_id_mapping = dict()
  670. ssh_keys_id = list()
  671. for row in rows:
  672. if row['ssh_key_id'] not in ssh_keys_id:
  673. ssh_keys_id.append(row['ssh_key_id'].__str__())
  674. if row['guest_uuid'] not in guest_uuid_ssh_key_id_mapping:
  675. guest_uuid_ssh_key_id_mapping[row['guest_uuid']] = list()
  676. guest_uuid_ssh_key_id_mapping[row['guest_uuid']].append(row['ssh_key_id'])
  677. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  678. ssh_key_id_mapping = dict()
  679. for row in rows:
  680. row['url'] = url_for('v_ssh_keys.show')
  681. ssh_key_id_mapping[row['id']] = row
  682. rows, _ = Snapshot.get_by_filter(filter_str=':'.join(['guest_uuid', 'in', ','.join(uuids)]))
  683. snapshots_guest_uuid_mapping = dict()
  684. for row in rows:
  685. guest_uuid = row['guest_uuid']
  686. if guest_uuid not in snapshots_guest_uuid_mapping:
  687. snapshots_guest_uuid_mapping[guest_uuid] = list()
  688. snapshots_guest_uuid_mapping[guest_uuid].append(row)
  689. for i, guest in enumerate(ret['data']):
  690. guest_uuid = ret['data'][i]['uuid']
  691. if 'ssh_keys' not in ret['data'][i]:
  692. ret['data'][i]['ssh_keys'] = list()
  693. if guest_uuid in guest_uuid_ssh_key_id_mapping:
  694. for ssh_key_id in guest_uuid_ssh_key_id_mapping[guest_uuid]:
  695. if ssh_key_id not in ssh_key_id_mapping:
  696. continue
  697. ret['data'][i]['ssh_keys'].append(ssh_key_id_mapping[ssh_key_id])
  698. if 'snapshot' not in ret['data'][i]:
  699. ret['data'][i]['snapshot'] = {
  700. 'creatable': True,
  701. 'mapping': list()
  702. }
  703. if guest_uuid in snapshots_guest_uuid_mapping:
  704. ret['data'][i]['snapshot']['mapping'] = snapshots_guest_uuid_mapping[guest_uuid]
  705. for snapshot in snapshots_guest_uuid_mapping[guest_uuid]:
  706. if snapshot['progress'] == 100:
  707. continue
  708. else:
  709. ret['data'][i]['snapshot']['creatable'] = False
  710. return ret
  711. @Utils.dumps2response
  712. def r_distribute_count():
  713. from models import Guest
  714. rows, count = Guest.get_all()
  715. ret = dict()
  716. ret['state'] = ji.Common.exchange_state(20000)
  717. ret['data'] = {
  718. 'os_template_image_id': dict(),
  719. 'status': dict(),
  720. 'node_id': dict(),
  721. 'cpu_memory': dict(),
  722. 'cpu': 0,
  723. 'memory': 0,
  724. 'guests': rows.__len__()
  725. }
  726. for guest in rows:
  727. if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
  728. ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
  729. if guest['status'] not in ret['data']['status']:
  730. ret['data']['status'][guest['status']] = 0
  731. if guest['node_id'] not in ret['data']['node_id']:
  732. ret['data']['node_id'][guest['node_id']] = 0
  733. cpu_memory = '_'.join([str(guest['cpu']), str(guest['memory'])])
  734. if cpu_memory not in ret['data']['cpu_memory']:
  735. ret['data']['cpu_memory'][cpu_memory] = 0
  736. ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
  737. ret['data']['status'][guest['status']] += 1
  738. ret['data']['node_id'][guest['node_id']] += 1
  739. ret['data']['cpu_memory'][cpu_memory] += 1
  740. ret['data']['cpu'] += guest['cpu']
  741. ret['data']['memory'] += guest['memory']
  742. return ret
  743. @Utils.dumps2response
  744. def r_update(uuid):
  745. args_rules = [
  746. Rules.UUID.value
  747. ]
  748. if 'remark' in request.json:
  749. args_rules.append(
  750. Rules.REMARK.value,
  751. )
  752. if args_rules.__len__() < 2:
  753. ret = dict()
  754. ret['state'] = ji.Common.exchange_state(20000)
  755. return ret
  756. request.json['uuid'] = uuid
  757. try:
  758. ji.Check.previewing(args_rules, request.json)
  759. guest = Guest()
  760. guest.uuid = uuid
  761. guest.get_by('uuid')
  762. guest.remark = request.json.get('remark', guest.label)
  763. guest.update()
  764. guest.get()
  765. ret = dict()
  766. ret['state'] = ji.Common.exchange_state(20000)
  767. ret['data'] = guest.__dict__
  768. return ret
  769. except ji.PreviewingError, e:
  770. return json.loads(e.message)
  771. @Utils.dumps2response
  772. def r_reset_password(uuids, password):
  773. args_rules = [
  774. Rules.UUIDS.value,
  775. Rules.PASSWORD.value
  776. ]
  777. try:
  778. ji.Check.previewing(args_rules, {'uuids': uuids, 'password': password})
  779. guest = Guest()
  780. os_template_image = OSTemplateImage()
  781. os_template_profile = OSTemplateProfile()
  782. # 检测所指定的 UUDIs 实例都存在
  783. for uuid in uuids.split(','):
  784. guest.uuid = uuid
  785. guest.get_by('uuid')
  786. for uuid in uuids.split(','):
  787. guest.uuid = uuid
  788. guest.get_by('uuid')
  789. os_template_image.id = guest.os_template_image_id
  790. os_template_image.get()
  791. os_template_profile.id = os_template_image.os_template_profile_id
  792. os_template_profile.get()
  793. user = 'root'
  794. if os_template_profile.os_type == 'windows':
  795. user = 'administrator'
  796. # guest.password 由 guest 事件处理机更新。参见 @models/event_processory.py:189 附近。
  797. message = {
  798. '_object': 'guest',
  799. 'action': 'reset_password',
  800. 'uuid': guest.uuid,
  801. 'node_id': guest.node_id,
  802. 'os_type': os_template_profile.os_type,
  803. 'user': user,
  804. 'password': password,
  805. 'passback_parameters': {'password': password}
  806. }
  807. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  808. ret = dict()
  809. ret['state'] = ji.Common.exchange_state(20000)
  810. return ret
  811. except ji.PreviewingError, e:
  812. return json.loads(e.message)
  813. @Utils.dumps2response
  814. def r_allocate_bandwidth(uuids, bandwidth, bandwidth_unit):
  815. args_rules = [
  816. Rules.UUIDS.value,
  817. Rules.BANDWIDTH_IN_URL.value,
  818. Rules.BANDWIDTH_UNIT.value,
  819. ]
  820. try:
  821. ji.Check.previewing(args_rules, {'uuids': uuids, 'bandwidth': bandwidth, 'bandwidth_unit': bandwidth_unit})
  822. bandwidth = int(bandwidth)
  823. if bandwidth_unit == 'k':
  824. bandwidth = bandwidth * 1000
  825. elif bandwidth_unit == 'm':
  826. bandwidth = bandwidth * 1000 ** 2
  827. elif bandwidth_unit == 'g':
  828. bandwidth = bandwidth * 1000 ** 3
  829. else:
  830. ret = dict()
  831. ret['state'] = ji.Common.exchange_state(41203)
  832. raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))
  833. guest = Guest()
  834. # 检测所指定的 UUDIs 实例都存在
  835. for uuid in uuids.split(','):
  836. guest.uuid = uuid
  837. guest.get_by('uuid')
  838. for uuid in uuids.split(','):
  839. guest.uuid = uuid
  840. guest.get_by('uuid')
  841. guest.bandwidth = bandwidth
  842. message = {
  843. '_object': 'guest',
  844. 'action': 'allocate_bandwidth',
  845. 'uuid': guest.uuid,
  846. 'node_id': guest.node_id,
  847. 'bandwidth': guest.bandwidth,
  848. 'passback_parameters': {'bandwidth': guest.bandwidth}
  849. }
  850. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  851. ret = dict()
  852. ret['state'] = ji.Common.exchange_state(20000)
  853. return ret
  854. except ji.PreviewingError, e:
  855. return json.loads(e.message)