QuickRecovery.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{$t('compute.server.quick.recovery')}}</div>
  4. <div slot="body">
  5. <dialog-selected-tips :name="$t('dictionary.server')" :count="params.data.length" :action="$t('compute.server.quick.recovery')" />
  6. <dialog-table :data="params.data" :columns="columns" />
  7. <a-form :form="form.fc" hideRequiredMark v-bind="formItemLayout">
  8. <!-- 自动启动 -->
  9. <a-form-item :label="$t('compute.text_494')" v-if="isSingle && firstData.status === 'ready'" :extra="$t('compute.text_1263')">
  10. <a-switch
  11. :checkedChildren="$t('compute.text_115')"
  12. :unCheckedChildren="$t('compute.text_116')"
  13. v-decorator="decorators.auto_start" />
  14. </a-form-item>
  15. <a-form-item
  16. :label="$t('compute.text_111')"
  17. :validate-status="message ? 'warning' : hostValidateStatus"
  18. :help="message || hostValidateMsg">
  19. <list-select
  20. v-decorator="decorators.host"
  21. :list-props="resourceProps"
  22. :formatter="v => v.name"
  23. :multiple="false"
  24. :placeholder="$t('compute.text_314')"
  25. :dialog-params="{ title: $t('compute.text_111'), width: 1060 }"
  26. @change="hostChangeHandle" />
  27. </a-form-item>
  28. </a-form>
  29. </div>
  30. <div slot="footer">
  31. <a-button type="primary" @click="handleConfirm" :loading="loading" :disabled="handleConfirmDisabled">{{ $t('dialog.ok') }}</a-button>
  32. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  33. </div>
  34. </base-dialog>
  35. </template>
  36. <script>
  37. import { mapGetters } from 'vuex'
  38. import DialogMixin from '@/mixins/dialog'
  39. import WindowsMixin from '@/mixins/windows'
  40. import ListSelect from '@/sections/ListSelect'
  41. import ResourceProps from '../mixins/resourceProps'
  42. export default {
  43. name: 'VmQuickRecoveryDialog',
  44. components: {
  45. ListSelect,
  46. },
  47. mixins: [DialogMixin, WindowsMixin, ResourceProps],
  48. data () {
  49. return {
  50. loading: false,
  51. form: {
  52. fc: this.$form.createForm(this),
  53. },
  54. forcastData: null,
  55. hosts: [],
  56. message: '',
  57. decorators: {
  58. host: [
  59. 'host',
  60. {
  61. rules: [
  62. { required: false, message: this.$t('compute.text_314'), trigger: 'change' },
  63. ],
  64. },
  65. ],
  66. auto_start: [
  67. 'auto_start',
  68. {
  69. initialValue: true,
  70. valuePropName: 'checked',
  71. },
  72. ],
  73. },
  74. formItemLayout: {
  75. wrapperCol: {
  76. span: 20,
  77. },
  78. labelCol: {
  79. span: 4,
  80. },
  81. },
  82. }
  83. },
  84. computed: {
  85. ...mapGetters(['scope', 'isAdminMode']),
  86. firstData () {
  87. return this.params.data[0]
  88. },
  89. isSingle () {
  90. return this.params.data.length === 1
  91. },
  92. hostsParams () {
  93. let hostType = 'hypervisor'
  94. const hostIds = this.forcastData?.filtered_candidates?.map(v => v.id) || []
  95. if (this.firstData.hypervisor !== 'kvm') {
  96. hostType = this.firstData.hypervisor
  97. }
  98. const ret = {
  99. scope: this.scope,
  100. host_type: hostType,
  101. limit: 10,
  102. enabled: 1,
  103. host_status: 'online',
  104. server_id_for_network: this.firstData.id,
  105. os_arch: this.firstData.os_arch,
  106. }
  107. if (this.isAdminMode && this.isSingle) {
  108. ret.project_domain = this.params.data[0].domain_id
  109. }
  110. if (hostIds && hostIds.length > 0) {
  111. ret.filter = `id.notin(${hostIds.join(',')})`
  112. }
  113. return ret
  114. },
  115. hostsOptions () {
  116. const hostIds = this.forcastData?.filtered_candidates?.map(v => v.id) || []
  117. if (this.forcastData?.can_create === false) {
  118. return []
  119. }
  120. return this.hosts.filter(v => {
  121. return !hostIds.includes(v.id) && v.id !== this.firstData.host_id
  122. }).map(v => {
  123. return {
  124. key: v.id,
  125. label: v.name,
  126. }
  127. })
  128. },
  129. hostValidateStatus () {
  130. if (this.forcastData && this.hostsOptions?.length === 0) {
  131. return 'error'
  132. }
  133. return 'success'
  134. },
  135. hostValidateMsg () {
  136. if (this.forcastData && this.hostsOptions?.length === 0) {
  137. return this.$t('compute.transfer_host')
  138. }
  139. return this.$t('compute.text_1384')
  140. },
  141. handleConfirmDisabled () {
  142. return this.forcastData && this.hostsOptions?.length === 0
  143. },
  144. columns () {
  145. const fields = ['name', 'status', 'host']
  146. return this.params.columns.filter(item => {
  147. const { field } = item
  148. return fields.indexOf(field) > -1
  149. })
  150. },
  151. },
  152. created () {
  153. this.isSingle && this.queryForcastData()
  154. this.queryHosts()
  155. },
  156. methods: {
  157. doSingleTransfer (ids, values) {
  158. const data = {
  159. prefer_host: values.host,
  160. rescue_mode: true,
  161. }
  162. if (this.firstData.status === 'ready') {
  163. data.auto_start = values.auto_start
  164. }
  165. return this.params.onManager('performAction', {
  166. id: this.firstData.id,
  167. steadyStatus: ['running', 'ready'],
  168. managerArgs: {
  169. action: 'migrate',
  170. data,
  171. },
  172. })
  173. },
  174. doBatchTransfer (ids, values) {
  175. const data = {
  176. guest_ids: ids,
  177. prefer_host: values.host,
  178. rescue_mode: true,
  179. }
  180. return this.params.onManager('performClassAction', {
  181. id: ids,
  182. steadyStatus: ['running', 'ready'],
  183. managerArgs: {
  184. action: 'batch-migrate',
  185. data,
  186. },
  187. })
  188. },
  189. async handleConfirm () {
  190. this.loading = true
  191. try {
  192. const values = await this.form.fc.validateFields()
  193. const ids = this.params.data.map(item => item.id)
  194. if (this.isSingle) {
  195. await this.doSingleTransfer(ids, values)
  196. } else {
  197. await this.doBatchTransfer(ids, values)
  198. }
  199. this.cancelDialog()
  200. } finally {
  201. this.loading = false
  202. }
  203. },
  204. doForecast (prefer_host_id) {
  205. const manager = new this.$Manager('servers')
  206. const params = {
  207. live_migrate: false,
  208. is_rescue_mode: true,
  209. }
  210. if (prefer_host_id) {
  211. params.prefer_host_id = prefer_host_id
  212. }
  213. return manager.performAction({
  214. id: this.params.data[0].id,
  215. action: 'migrate-forecast',
  216. data: params,
  217. })
  218. },
  219. queryForcastData (prefer_host_id) {
  220. this.doForecast(prefer_host_id).then((res) => {
  221. this.forcastData = res.data
  222. }).catch((err) => {
  223. console.log(err)
  224. throw err
  225. })
  226. },
  227. queryHosts () {
  228. const hostsManager = new this.$Manager('hosts')
  229. hostsManager.list({ params: this.hostsParams }).then((res) => {
  230. this.hosts = res.data.data || []
  231. }).catch((err) => {
  232. console.log(err)
  233. throw err
  234. })
  235. },
  236. hostChangeHandle (hostId) {
  237. const hostArr = this.params.data.filter(v => v.host_id === hostId)
  238. if (hostArr.length > 0) {
  239. this.message = this.$t('compute.transfer_mutiple_dialog_alert', [hostArr.length])
  240. } else {
  241. this.message = ''
  242. }
  243. },
  244. },
  245. }
  246. </script>