ssh_key.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from math import ceil
  4. import requests
  5. from flask import Blueprint, url_for
  6. from flask import request
  7. from werkzeug.datastructures import ImmutableMultiDict
  8. import json
  9. import jimit as ji
  10. from jimvc.api.base import Base
  11. from jimvc.models import SSHKey, OSTemplateImage, OSTemplateProfile, GuestState
  12. from jimvc.models import SSHKeyGuestMapping
  13. from jimvc.models import Guest
  14. from jimvc.models import Utils
  15. from jimvc.models import Rules
  16. __author__ = 'James Iter'
  17. __date__ = '2018/2/26'
  18. __contact__ = 'james.iter.cn@gmail.com'
  19. __copyright__ = '(c) 2018 by James Iter.'
  20. blueprint = Blueprint(
  21. 'api_ssh_key',
  22. __name__,
  23. url_prefix='/api/ssh_key'
  24. )
  25. blueprints = Blueprint(
  26. 'api_ssh_keys',
  27. __name__,
  28. url_prefix='/api/ssh_keys'
  29. )
  30. ssh_key_base = Base(the_class=SSHKey, the_blueprint=blueprint, the_blueprints=blueprints)
  31. guest_base = Base(the_class=Guest, the_blueprint=blueprint, the_blueprints=blueprints)
  32. @Utils.dumps2response
  33. def r_create():
  34. args_rules = [
  35. Rules.LABEL.value,
  36. Rules.PUBLIC_KEY.value
  37. ]
  38. try:
  39. ret = dict()
  40. ret['state'] = ji.Common.exchange_state(20000)
  41. ji.Check.previewing(args_rules, request.json)
  42. ssh_key = SSHKey()
  43. ssh_key.label = request.json.get('label')
  44. ssh_key.public_key = request.json.get('public_key')
  45. if ssh_key.exist_by('public_key'):
  46. ret['state'] = ji.Common.exchange_state(40901)
  47. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', ssh_key.public_key])
  48. return ret
  49. ssh_key.create()
  50. return ret
  51. except ji.PreviewingError, e:
  52. return json.loads(e.message)
  53. @Utils.dumps2response
  54. def r_update(_id):
  55. ssh_key = SSHKey()
  56. args_rules = [
  57. Rules.ID.value
  58. ]
  59. if 'label' in request.json:
  60. args_rules.append(
  61. Rules.LABEL.value,
  62. )
  63. if 'public_key' in request.json:
  64. args_rules.append(
  65. Rules.PUBLIC_KEY.value,
  66. )
  67. if args_rules.__len__() < 2:
  68. ret = dict()
  69. ret['state'] = ji.Common.exchange_state(20000)
  70. return ret
  71. request.json['id'] = _id
  72. try:
  73. ji.Check.previewing(args_rules, request.json)
  74. ssh_key.id = request.json.get('id')
  75. ssh_key.get()
  76. ssh_key.label = request.json.get('label', ssh_key.label)
  77. ssh_key.public_key = request.json.get('public_key', ssh_key.public_key)
  78. ssh_key.update()
  79. ssh_key.get()
  80. ret = dict()
  81. ret['state'] = ji.Common.exchange_state(20000)
  82. ret['data'] = ssh_key.__dict__
  83. return ret
  84. except ji.PreviewingError, e:
  85. return json.loads(e.message)
  86. @Utils.dumps2response
  87. def r_get(ids):
  88. return ssh_key_base.get(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
  89. @Utils.dumps2response
  90. def r_get_by_filter():
  91. return ssh_key_base.get_by_filter()
  92. @Utils.dumps2response
  93. def r_content_search():
  94. return ssh_key_base.content_search()
  95. def update_ssh_key(uuid):
  96. guest = Guest()
  97. guest.uuid = uuid
  98. guest.get_by('uuid')
  99. # 不支持更新离线虚拟机的 SSH-KEY
  100. if guest.status != GuestState.running.value:
  101. return
  102. os_template_image = OSTemplateImage()
  103. os_template_profile = OSTemplateProfile()
  104. os_template_image.id = guest.os_template_image_id
  105. os_template_image.get()
  106. os_template_profile.id = os_template_image.os_template_profile_id
  107. os_template_profile.get()
  108. # 不支持更新 Windows 虚拟机的 SSH-KEY
  109. if os_template_profile.os_type == 'windows':
  110. return
  111. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', uuid]))
  112. ssh_keys_id = list()
  113. for row in rows:
  114. ssh_keys_id.append(row['ssh_key_id'].__str__())
  115. ssh_keys = list()
  116. if ssh_keys_id.__len__() > 0:
  117. rows, _ = SSHKey.get_by_filter(filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
  118. for row in rows:
  119. ssh_keys.append(row['public_key'])
  120. else:
  121. ssh_keys.append('')
  122. message = {
  123. '_object': 'guest',
  124. 'uuid': uuid,
  125. 'node_id': guest.node_id,
  126. 'action': 'update_ssh_key',
  127. 'ssh_keys': ssh_keys,
  128. 'os_type': os_template_profile.os_type,
  129. 'passback_parameters': {'uuid': uuid, 'ssh_keys': ssh_keys, 'os_type': os_template_profile.os_type}
  130. }
  131. Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
  132. @Utils.dumps2response
  133. def r_delete(ids):
  134. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'in', ids]))
  135. SSHKeyGuestMapping.delete_by_filter(filter_str=':'.join(['ssh_key_id', 'in', ids]))
  136. for row in rows:
  137. update_ssh_key(uuid=row['guest_uuid'])
  138. return ssh_key_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
  139. @Utils.dumps2response
  140. def r_bound(ssh_key_id):
  141. args_rules = [
  142. Rules.SSH_KEY_ID_EXT.value,
  143. ]
  144. try:
  145. ji.Check.previewing(args_rules, {'ssh_key_id': ssh_key_id})
  146. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  147. guests_uuid = list()
  148. for row in rows:
  149. guests_uuid.append(row['guest_uuid'])
  150. if guests_uuid.__len__() == 0:
  151. guests_uuid.append('_')
  152. request.__setattr__('args', ImmutableMultiDict([
  153. ('filter', ':'.join(['uuid', 'in', ','.join(guests_uuid)])),
  154. ('page_size', 10000)
  155. ]))
  156. return guest_base.get_by_filter()
  157. except ji.PreviewingError, e:
  158. return json.loads(e.message)
  159. @Utils.dumps2response
  160. def r_unbound(ssh_key_id):
  161. args_rules = [
  162. Rules.SSH_KEY_ID_EXT.value,
  163. ]
  164. try:
  165. ji.Check.previewing(args_rules, {'ssh_key_id': ssh_key_id})
  166. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  167. guests_uuid = list()
  168. for row in rows:
  169. guests_uuid.append(row['guest_uuid'])
  170. if guests_uuid.__len__() == 0:
  171. guests_uuid.append('_')
  172. request.__setattr__('args', ImmutableMultiDict([
  173. ('filter', ':'.join(['uuid', 'notin', ','.join(guests_uuid)])),
  174. ('page_size', 10000)
  175. ]))
  176. return guest_base.get_by_filter()
  177. except ji.PreviewingError, e:
  178. return json.loads(e.message)
  179. @Utils.dumps2response
  180. def r_bind(ssh_key_id, uuids):
  181. args_rules = [
  182. Rules.SSH_KEY_ID_EXT.value,
  183. Rules.UUIDS.value
  184. ]
  185. try:
  186. ji.Check.previewing(args_rules, {'ssh_key_id': ssh_key_id, 'uuids': uuids})
  187. ret = dict()
  188. ret['state'] = ji.Common.exchange_state(20000)
  189. ssh_key = SSHKey()
  190. ssh_key.id = ssh_key_id
  191. # 判断 ssh_key id 为 ssh_key_id 的对象是否存在
  192. if not ssh_key.exist():
  193. ret['state'] = ji.Common.exchange_state(40401)
  194. ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ssh_key_id: ', ssh_key_id])
  195. return ret
  196. # 获取已经和该 ssh_key 绑定过的 guest uuid 集合,用于判断是否已经绑定过该 ssh_key,避免重复绑定
  197. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  198. guests_uuid = list()
  199. for row in rows:
  200. guests_uuid.append(row['guest_uuid'])
  201. ssh_key_guest_mapping = SSHKeyGuestMapping()
  202. for uuid in uuids.split(','):
  203. # 如果已经绑定过,则忽略
  204. if uuid in guests_uuid:
  205. continue
  206. ssh_key_guest_mapping.ssh_key_id = ssh_key_id
  207. ssh_key_guest_mapping.guest_uuid = uuid
  208. ssh_key_guest_mapping.create()
  209. update_ssh_key(uuid=uuid)
  210. # 返回执行结果
  211. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  212. guests_uuid = list()
  213. for row in rows:
  214. guests_uuid.append(row['guest_uuid'])
  215. if guests_uuid.__len__() == 0:
  216. guests_uuid.append('_')
  217. request.__setattr__('args', ImmutableMultiDict([
  218. ('filter', ':'.join(['uuid', 'in', ','.join(guests_uuid)])),
  219. ('page_size', 10000)
  220. ]))
  221. return guest_base.get_by_filter()
  222. except ji.PreviewingError, e:
  223. return json.loads(e.message)
  224. @Utils.dumps2response
  225. def r_unbind(ssh_key_id, uuids):
  226. args_rules = [
  227. Rules.SSH_KEY_ID_EXT.value,
  228. Rules.UUIDS.value
  229. ]
  230. try:
  231. ji.Check.previewing(args_rules, {'ssh_key_id': ssh_key_id, 'uuids': uuids})
  232. ret = dict()
  233. ret['state'] = ji.Common.exchange_state(20000)
  234. # 获取已经和该 ssh_key 绑定过的映射集合,从中获取映射 id,用于解绑操作
  235. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  236. guests_uuid = uuids.split(',')
  237. ssh_key_guest_mapping = SSHKeyGuestMapping()
  238. for row in rows:
  239. # 解除已经绑定过的 guest
  240. if row['guest_uuid'] in guests_uuid:
  241. ssh_key_guest_mapping.id = row['id']
  242. ssh_key_guest_mapping.delete()
  243. update_ssh_key(uuid=row['guest_uuid'])
  244. # 返回执行结果
  245. rows, _ = SSHKeyGuestMapping.get_by_filter(filter_str=':'.join(['ssh_key_id', 'eq', ssh_key_id]))
  246. guests_uuid = list()
  247. for row in rows:
  248. guests_uuid.append(row['guest_uuid'])
  249. if guests_uuid.__len__() == 0:
  250. guests_uuid.append('_')
  251. request.__setattr__('args', ImmutableMultiDict([
  252. ('filter', ':'.join(['uuid', 'in', ','.join(guests_uuid)])),
  253. ('page_size', 10000)
  254. ]))
  255. return guest_base.get_by_filter()
  256. except ji.PreviewingError, e:
  257. return json.loads(e.message)
  258. @Utils.dumps2response
  259. def r_show():
  260. args = list()
  261. page = int(request.args.get('page', 1))
  262. page_size = int(request.args.get('page_size', 10))
  263. keyword = request.args.get('keyword', None)
  264. order_by = request.args.get('order_by', None)
  265. order = request.args.get('order', None)
  266. if page is not None:
  267. args.append('page=' + page.__str__())
  268. if page_size is not None:
  269. args.append('page_size=' + page_size.__str__())
  270. if keyword is not None:
  271. args.append('keyword=' + keyword.__str__())
  272. if order_by is not None:
  273. args.append('order_by=' + order_by)
  274. if order is not None:
  275. args.append('order=' + order)
  276. ssh_keys_url = url_for('api_ssh_keys.r_get_by_filter', _external=True)
  277. if keyword is not None:
  278. ssh_keys_url = url_for('api_ssh_keys.r_content_search', _external=True)
  279. if args.__len__() > 0:
  280. ssh_keys_url = ssh_keys_url + '?' + '&'.join(args)
  281. ssh_keys_ret = requests.get(url=ssh_keys_url, cookies=request.cookies)
  282. ssh_keys_ret = json.loads(ssh_keys_ret.content)
  283. last_page = int(ceil(ssh_keys_ret['paging']['total'] / float(page_size)))
  284. page_length = 5
  285. pages = list()
  286. if page < int(ceil(page_length / 2.0)):
  287. for i in range(1, page_length + 1):
  288. pages.append(i)
  289. if i == last_page or last_page == 0:
  290. break
  291. elif last_page - page < page_length / 2:
  292. for i in range(last_page - page_length + 1, last_page + 1):
  293. if i < 1:
  294. continue
  295. pages.append(i)
  296. else:
  297. for i in range(page - page_length / 2, page + int(ceil(page_length / 2.0))):
  298. pages.append(i)
  299. if i == last_page or last_page == 0:
  300. break
  301. ret = dict()
  302. ret['state'] = ji.Common.exchange_state(20000)
  303. ret['data'] = {
  304. 'ssh_keys': ssh_keys_ret['data'],
  305. 'paging': ssh_keys_ret['paging'],
  306. 'page': page,
  307. 'page_size': page_size,
  308. 'keyword': keyword,
  309. 'pages': pages,
  310. 'last_page': last_page,
  311. 'order_by': order_by,
  312. 'order': order
  313. }
  314. return ret