Clone.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{$t('compute.text_359')}}</div>
  4. <div slot="body">
  5. <dialog-selected-tips :name="$t('dictionary.server')" :count="params.data.length" :action="$t('compute.text_359')" />
  6. <dialog-table :data="params.data" :columns="params.columns.slice(0, 3)" />
  7. <a-form :form="form.fc" hideRequiredMark v-bind="formItemLayout">
  8. <a-form-item :label="$t('compute.text_228')">
  9. <a-input v-decorator="decorators.name" @change="e => { form.fi.generate_name = e.target.value }" />
  10. <template #extra>
  11. <name-repeated
  12. res="servers"
  13. :name="form.fi.generate_name"
  14. :default-text="$t('compute.text_893')" />
  15. </template>
  16. </a-form-item>
  17. <a-form-item :label="$t('compute.text_294')">
  18. <a-input-number v-decorator="decorators.__count__" :min="1" :max="10" :step="1" :parser="Math.round" />
  19. </a-form-item>
  20. <a-form-item :label="$t('compute.text_267')" v-if="isPublic">
  21. <os-select
  22. :type="type"
  23. :form="form"
  24. :types="osTypes"
  25. :osType="osType"
  26. :hypervisor="firstData.hypervisor"
  27. :image-params="imageParams"
  28. :cache-image-params="cacheImageParams"
  29. :cloudproviderParamsExtra="cloudproviderParamsExtra"
  30. :decorator="decorators.imageOS" />
  31. </a-form-item>
  32. <a-form-item :label="$t('compute.text_1041')" v-if="isOpenWorkflow">
  33. <a-input v-decorator="decorators.reason" :placeholder="$t('compute.text_1105')" />
  34. </a-form-item>
  35. </a-form>
  36. </div>
  37. <div slot="footer">
  38. <a-button type="primary" @click="handleConfirm" :loading="loading">{{ confirmText }}</a-button>
  39. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  40. </div>
  41. </base-dialog>
  42. </template>
  43. <script>
  44. import * as R from 'ramda'
  45. import _ from 'lodash'
  46. import { mapGetters } from 'vuex'
  47. import { SERVER_TYPE } from '@Compute/constants'
  48. import OsSelect from '@Compute/sections/OsSelect'
  49. import DialogMixin from '@/mixins/dialog'
  50. import WindowsMixin from '@/mixins/windows'
  51. import WorkflowMixin from '@/mixins/workflow'
  52. import { findPlatform } from '@/utils/common/hypervisor'
  53. import { IMAGES_TYPE_MAP } from '@/constants/compute'
  54. import NameRepeated from '@/sections/NameRepeated'
  55. import { HYPERVISORS_MAP } from '@/constants'
  56. export default {
  57. name: 'VmCloneDialog',
  58. components: {
  59. OsSelect,
  60. NameRepeated,
  61. },
  62. mixins: [DialogMixin, WindowsMixin, WorkflowMixin],
  63. provide () {
  64. return {
  65. form: this.form,
  66. }
  67. },
  68. data () {
  69. return {
  70. loading: false,
  71. form: {
  72. fc: this.$form.createForm(this, {
  73. onValuesChange: (props, values) => {
  74. if (values.hasOwnProperty('imageType')) {
  75. this.form.fi.imageType = values.imageType
  76. }
  77. },
  78. }),
  79. fi: {
  80. generate_name: '',
  81. imageType: IMAGES_TYPE_MAP.public.key,
  82. },
  83. fd: {},
  84. },
  85. decorators: {
  86. name: [
  87. 'name',
  88. {
  89. validateFirst: true,
  90. rules: [
  91. { required: true, message: this.$t('compute.text_210') },
  92. ],
  93. },
  94. ],
  95. __count__: [
  96. '__count__',
  97. {
  98. initialValue: 1,
  99. rules: [
  100. { required: true, message: this.$t('compute.text_1195') },
  101. ],
  102. },
  103. ],
  104. imageOS: {
  105. prefer_manager: [
  106. 'prefer_manager',
  107. {
  108. rules: [
  109. { required: true, message: this.$t('compute.text_149') },
  110. ],
  111. },
  112. ],
  113. os: [
  114. 'os',
  115. {
  116. rules: [
  117. { required: true, message: this.$t('compute.text_153') },
  118. ],
  119. },
  120. ],
  121. image: [
  122. 'image',
  123. {
  124. rules: [
  125. { required: true, message: this.$t('compute.text_214') },
  126. ],
  127. },
  128. ],
  129. imageType: [
  130. 'imageType',
  131. {
  132. initialValue: IMAGES_TYPE_MAP.public.key,
  133. },
  134. ],
  135. },
  136. reason: [
  137. 'reason',
  138. {
  139. initialValue: '',
  140. },
  141. ],
  142. },
  143. formItemLayout: {
  144. wrapperCol: {
  145. span: 20,
  146. },
  147. labelCol: {
  148. span: 4,
  149. },
  150. },
  151. servers: [],
  152. createParams: {},
  153. specificationList: [],
  154. cloudproviderParamsExtra: {
  155. provider: _.get(this.params, 'data[0].provider'),
  156. },
  157. }
  158. },
  159. computed: {
  160. ...mapGetters(['scope', 'isAdminMode', 'userInfo']),
  161. firstData () {
  162. return this.params.data[0]
  163. },
  164. type () {
  165. return findPlatform(this.firstData.hypervisor)
  166. },
  167. isPublic () {
  168. return this.type === SERVER_TYPE.public
  169. },
  170. imageParams () {
  171. const params = {
  172. status: 'active',
  173. details: true,
  174. limit: 0,
  175. 'filter.0': 'disk_format.notequals(iso)',
  176. scope: this.scope,
  177. is_standard: true,
  178. }
  179. if (this.form.fi.imageType === IMAGES_TYPE_MAP.standard.key) {
  180. return params
  181. }
  182. if (this.form.fi.imageType === IMAGES_TYPE_MAP.customize.key) {
  183. params.is_standard = false
  184. return params
  185. }
  186. return params
  187. },
  188. cacheImageParams () {
  189. const params = {
  190. details: false,
  191. order_by: 'ref_count',
  192. order: 'desc',
  193. zone: this.firstData.zone_id,
  194. }
  195. return params
  196. },
  197. osType () {
  198. return this.params.data[0].os_type
  199. },
  200. osTypes () {
  201. if (HYPERVISORS_MAP.ctyun.key === this.firstData.hypervisor) {
  202. return ['public', 'public_customize']
  203. }
  204. return []
  205. },
  206. isOpenWorkflow () {
  207. return this.checkWorkflowEnabled(this.WORKFLOW_TYPES.APPLY_MACHINE)
  208. },
  209. confirmText () {
  210. return this.isOpenWorkflow ? this.$t('compute.text_288') : this.$t('dialog.ok')
  211. },
  212. },
  213. created () {
  214. this.manager = new this.$Manager('servers')
  215. this.fetchServerCreateParams()
  216. if (this.params.type === 'baremetal') {
  217. this.fetchHosts()
  218. }
  219. },
  220. methods: {
  221. async fetchServerCreateParams () {
  222. const params = {
  223. id: this.firstData.id,
  224. spec: 'create-params',
  225. }
  226. try {
  227. const response = await this.manager.getSpecific(params)
  228. const data = response.data || {}
  229. this.createParams = data
  230. } catch (error) {
  231. throw error
  232. }
  233. },
  234. async fetchHosts () {
  235. new this.$Manager('hosts').list({
  236. params: {
  237. enabled: 1,
  238. usable: true,
  239. is_empty: true,
  240. host_type: 'baremetal',
  241. scope: this.$store.getters.scope,
  242. },
  243. }).then(({ data = {} }) => {
  244. if (data.data.length) {
  245. this.specificationList = data.data
  246. }
  247. })
  248. },
  249. async handleConfirm () {
  250. this.loading = true
  251. try {
  252. const values = await this.form.fc.validateFields()
  253. const data = this.genCloneData(values)
  254. if (this.params.type === 'baremetal') {
  255. if (this.specificationList.length) {
  256. this.$message.warning(this.$t('compute.text_1196'))
  257. return
  258. } else {
  259. const specificationItem = this.specificationList.filter(item => { return item.id === data.host_id })
  260. if (R.isEmpty(specificationItem.spec) || R.isNil(specificationItem.spec)) {
  261. this.$message.warning(this.$t('compute.text_1197'))
  262. return
  263. }
  264. }
  265. }
  266. const schedulerManager = new this.$Manager('schedulers', 'v1')
  267. const schedulerReponse = await schedulerManager.rpc({
  268. methodname: 'DoForecast',
  269. params: data,
  270. })
  271. if (!schedulerReponse.data.can_create) {
  272. this.$message.warning(this.$t('compute.text_1198'))
  273. return
  274. }
  275. if (this.isOpenWorkflow) {
  276. const variables = {
  277. project: this.params.data[0].tenant_id,
  278. project_domain: this.params.data[0].domain_id,
  279. process_definition_key: this.WORKFLOW_TYPES.APPLY_MACHINE,
  280. initiator: this.userInfo.id,
  281. 'server-create-paramter': JSON.stringify(data),
  282. description: values.reason,
  283. }
  284. await this.createWorkflow(variables)
  285. this.$message.success(this.$t('compute.text_1045', [data.name]))
  286. this.$router.push('/workflow')
  287. } else {
  288. await this.params.onManager('create', {
  289. managerArgs: {
  290. data,
  291. },
  292. })
  293. }
  294. this.cancelDialog()
  295. } finally {
  296. this.loading = false
  297. }
  298. },
  299. genCloneData (fd) {
  300. const server = {
  301. ...this.generateCreateParams(this.createParams, fd),
  302. __count__: fd.__count__,
  303. name: fd.name,
  304. generate_name: fd.name,
  305. }
  306. const disks = this.createParams.disks
  307. if (disks && disks.length > 0) {
  308. disks.forEach((disk) => {
  309. if (disk.disk_type === 'data') {
  310. delete disk.image_id
  311. }
  312. })
  313. }
  314. delete server.eip
  315. if (this.isPublic) {
  316. server.disks[0].image_id = fd.image.key
  317. server.imageType = fd.imageType
  318. server.prefer_manager = fd.prefer_manager
  319. }
  320. return server
  321. },
  322. generateCreateParams (createParams, fd) {
  323. const delProps = ['domain', 'domain_id', 'project', 'project_domain', 'project_domain_id', 'project_id', 'tenant', 'tenant_id']
  324. delProps.forEach(v => {
  325. delete createParams[v]
  326. })
  327. createParams.project_id = this.params.data[0].tenant_id
  328. return createParams
  329. },
  330. },
  331. }
  332. </script>