DetectSSH.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{action}}</div>
  4. <div slot="body">
  5. <detect-ssh-table :params="params" @radio-change="handleRadioChange" :remote="true" />
  6. </div>
  7. <div slot="footer">
  8. <a-button type="primary" @click="cancelDialog" :loading="loading">{{ $t('dialog.ok') }}</a-button>
  9. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  10. </div>
  11. </base-dialog>
  12. </template>
  13. <script>
  14. import {
  15. getStatusTableColumn,
  16. } from '@/utils/common/tableColumn'
  17. import DialogMixin from '@/mixins/dialog'
  18. import WindowsMixin from '@/mixins/windows'
  19. export const DetectSshTable = {
  20. name: 'DetectSshTable',
  21. props: {
  22. params: {
  23. type: Object,
  24. required: true,
  25. },
  26. /* 显示单选框 */
  27. showRadioSelect: {
  28. type: Boolean,
  29. default: false,
  30. },
  31. /* 主动从服务器端发起探测 */
  32. remote: {
  33. type: Boolean,
  34. default: false,
  35. },
  36. ansibleTasks: {
  37. /* item { 'server_uuid': 'ansible_playbook_id' } */
  38. type: Object,
  39. default: () => ({}),
  40. },
  41. /**/
  42. maxColumns: {
  43. type: Number,
  44. default: 2,
  45. },
  46. },
  47. mixins: [DialogMixin, WindowsMixin],
  48. render (h, context) {
  49. const vxeGridEvents = {}
  50. if (this.showRadioSelect) {
  51. vxeGridEvents['radio-change'] = ({ row }) => { this.$emit('radio-change', row) }
  52. }
  53. return h('dialog-table', {
  54. props: {
  55. data: this.rows,
  56. columns: this.columns,
  57. vxeGridProps: {
  58. 'radio-config': {
  59. showHeaderOverflow: false,
  60. resizable: false,
  61. highlight: true,
  62. trigger: 'row',
  63. checkMethod: ({ row }) => { return row.status === 'running' },
  64. },
  65. 'tooltip-config': {
  66. enabled: this.showRadioSelect,
  67. contentMethod: ({ type, column, row, items, _columnIndex }) => {
  68. return row.status !== 'running' ? this.$t('compute.text_1282') : ''
  69. },
  70. },
  71. },
  72. vxeGridEvents: vxeGridEvents,
  73. },
  74. key: this.updated,
  75. })
  76. },
  77. data () {
  78. const columns = []
  79. if (this.showRadioSelect) {
  80. columns.push({
  81. type: 'radio',
  82. width: '40',
  83. })
  84. }
  85. columns.push(...this.params.columns.slice(0, this.maxColumns))
  86. columns.push(
  87. getStatusTableColumn({
  88. field: 'sshable_status',
  89. title: this.$t('compute.vminstance.detect_ssh_authentication.status'),
  90. minWidth: 130,
  91. statusModule: 'serversshable',
  92. slotCallback: row => {
  93. return [
  94. <div class='d-flex align-items-center text-truncate'>
  95. <status status={ row.sshable_status } statusModule='serversshable' />
  96. </div>,
  97. ]
  98. },
  99. }))
  100. const rows = Object.assign([], this.params.data)
  101. if (this.remote) {
  102. columns.push({
  103. field: 'details',
  104. title: this.$t('table.title.operation'),
  105. width: 70,
  106. slots: {
  107. default: ({ row, column }) => {
  108. let text = ''
  109. row.details.map((detail) => {
  110. try {
  111. if (typeof detail === 'string') {
  112. text += detail
  113. } else {
  114. text += JSON.stringify(detail, null, 4)
  115. }
  116. } catch (e) {
  117. text += detail
  118. }
  119. text += '\n=============================\n'
  120. })
  121. return [<a-button size='small' type='link'
  122. onClick={() => this.clickHandler(text)}>{this.$t('common.view')}</a-button>]
  123. },
  124. },
  125. })
  126. rows.map((r) => { r.sshable_status = ''; r.details = [] })
  127. } else {
  128. rows.map((r) => { r.sshable_status = r.sshable_last_state ? 'available' : 'unavailable'; r.details = [] })
  129. }
  130. return {
  131. updated: 0,
  132. rows: rows,
  133. columns: columns,
  134. manager: new this.$Manager('servers'),
  135. }
  136. },
  137. watch: {
  138. updated () {
  139. const stableStatus = ['available', 'unavailable', 'detect_failed']
  140. const detecting = !this.rows.every((row) => {
  141. return row.sshable_status && stableStatus.indexOf(row.sshable_status) >= 0
  142. })
  143. if (!detecting) {
  144. this.$emit('onDetecting', false)
  145. }
  146. },
  147. },
  148. created () {
  149. if (this.remote) {
  150. this.doDetectSshStatus()
  151. }
  152. },
  153. methods: {
  154. clickHandler (val) {
  155. this.createDialog('EventLogDialog', {
  156. data: val,
  157. })
  158. },
  159. async _waitAnsibleTaskFinished (server, taskId, maxTry) {
  160. try {
  161. while (maxTry > 0) {
  162. const { data = {} } = await new this.$Manager('ansibleplaybooks').get({ id: taskId })
  163. if (data.status === 'running') {
  164. await new Promise(resolve => setTimeout(resolve, 6000))
  165. maxTry -= 1
  166. } else {
  167. if (data.output) {
  168. server.details.push(data.output)
  169. }
  170. break
  171. }
  172. }
  173. } catch (e) {
  174. server.sshable_status = 'detect_failed'
  175. throw e
  176. }
  177. },
  178. async _checkServerSshable (server) {
  179. try {
  180. const { data = {} } = await this.manager.getSpecific({ id: server.id, spec: 'sshable' })
  181. server.details.push(data)
  182. server.sshable_status = data.method_tried && !data.method_tried.every((m) => { return m.sshable === false }) ? 'available' : 'unavailable'
  183. } catch (e) {
  184. server.sshable_status = 'detect_failed'
  185. throw e
  186. } finally {
  187. this.updated += 1
  188. }
  189. },
  190. checkServerSshable (server) {
  191. this.updated += 1
  192. server.sshable_status = 'detecting'
  193. this._checkServerSshable(server)
  194. },
  195. waitAnsibleTaskFinished (server, taskId, maxTry) {
  196. const self = this
  197. server.sshable_status = 'ansible_deploying'
  198. this._waitAnsibleTaskFinished(server, taskId, maxTry).then((ret) => {
  199. this.$nextTick(() => {
  200. self.checkServerSshable(server)
  201. })
  202. })
  203. },
  204. doDetectSshStatus () {
  205. if (this.rows && this.rows.length > 0) {
  206. this.$emit('onDetecting', true)
  207. }
  208. for (const r of this.rows) {
  209. if (this.ansibleTasks[r.id]) {
  210. this.waitAnsibleTaskFinished(r, this.ansibleTasks[r.id], 10)
  211. } else {
  212. this.checkServerSshable(r)
  213. }
  214. }
  215. },
  216. handleRadioChange (row) {
  217. this.$emit('radio-change', row)
  218. },
  219. },
  220. }
  221. export default {
  222. name: 'DetectSSHDialog',
  223. components: { DetectSshTable },
  224. mixins: [DialogMixin, WindowsMixin],
  225. data () {
  226. return {
  227. loading: false,
  228. action: this.$t('compute.vminstance.actions.detect_ssh_authentication'),
  229. }
  230. },
  231. }
  232. </script>