From fdd6dbb5e38e63bad84f9dc68a13e2a18b3147be Mon Sep 17 00:00:00 2001 From: zhangke Date: Thu, 13 Oct 2022 15:13:42 +0800 Subject: [PATCH] fix: Not show the data disk when creating vm by Instance snapshot Not show the data disk when creating vm by Instance snapshot with system disk and data disk 1. fetch instanceSnapshotDataVolumes data when change instance snapshot 2. add Required Data Disk form item of BaseStep when create instance by snapshot 3. show instance snapshot data disk's info if instance snapshot has system disk and data disk 4. hide render default system disk when selecting Bootable Volume of Start Source's option Closes-Bug: #1992739 Change-Id: I3a1daa8845cc9f73b8ea8d0216d49ad858b7168b --- src/locales/en.json | 1 + src/locales/zh.json | 1 + .../actions/StepCreate/BaseStep/index.jsx | 105 ++++++++++++------ src/resources/cinder/snapshot.jsx | 60 ++++++++++ src/stores/glance/instance-snapshot.js | 38 +++++++ 5 files changed, 170 insertions(+), 35 deletions(-) create mode 100644 src/resources/cinder/snapshot.jsx diff --git a/src/locales/en.json b/src/locales/en.json index 56d5953a..0e2e2833 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1940,6 +1940,7 @@ "Request ID": "Request ID", "Require": "Require", "Require(Need multithreading)": "Require(Need multithreading)", + "Required Data Disk": "Required Data Disk", "Rescue": "Rescue", "Rescued": "Rescued", "Rescuing": "Rescuing", diff --git a/src/locales/zh.json b/src/locales/zh.json index 2e47d273..cfbfabec 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1940,6 +1940,7 @@ "Request ID": "请求ID", "Require": "强制", "Require(Need multithreading)": "Require(必须有多线程)", + "Required Data Disk": "所需数据盘", "Rescue": "救援", "Rescued": "已救援", "Rescuing": "救援中", diff --git a/src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx b/src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx index e6e396c5..cef6794b 100644 --- a/src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx +++ b/src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx @@ -34,6 +34,11 @@ import { import Base from 'components/Form'; import InstanceVolume from 'components/FormItem/InstanceVolume'; import { isGpuCategory } from 'resources/nova/flavor'; +import { + volumeTypes, + getDiskInfo, + getInstanceSnapshotDataDisk, +} from 'resources/cinder/snapshot'; import FlavorSelectTable from '../../../components/FlavorSelectTable'; export class BaseStep extends Base { @@ -126,11 +131,7 @@ export class BaseStep extends Base { } get volumeTypes() { - return (this.volumeTypeStore.list.data || []).map((it) => ({ - label: it.name, - value: it.id, - originData: toJS(it), - })); + return volumeTypes(); } get volumes() { @@ -337,62 +338,53 @@ export class BaseStep extends Base { if (!id) { this.updateContext({ instanceSnapshotDisk: null, + instanceSnapshotDataVolumes: [], }); this.setState({ instanceSnapshotDisk: null, instanceSnapshotMinSize: 0, + instanceSnapshotDataVolumes: [], }); return; } - const detail = await this.instanceSnapshotStore.fetchDetail({ id }); + const detail = + await this.instanceSnapshotStore.fetchInstanceSnapshotVolumeData({ id }); const { - snapshotDetail: { size: snapshotSize = 0, volume_type_id } = {}, + snapshotDetail: { size: snapshotSize = 0 } = {}, block_device_mapping = '', volumeDetail, + snapshotDetail, + instanceSnapshotDataVolumes = [], } = detail; if (!volumeDetail) { this.updateContext({ instanceSnapshotDisk: null, + instanceSnapshotDataVolumes: [], }); this.setState({ instanceSnapshotDisk: null, instanceSnapshotMinSize: 0, + instanceSnapshotDataVolumes: [], }); } const minSize = Math.max(min_disk, size, snapshotSize); - let bdm = {}; - try { - bdm = JSON.parse(block_device_mapping); - } catch (e) {} - const { volume_type } = volumeDetail; - const { delete_on_termination } = bdm[0] || {}; - const deleteType = delete_on_termination ? 1 : 0; - const deleteTypeLabel = delete_on_termination - ? t('Deleted with the instance') - : t('Not deleted with the instance'); - const volumeTypeId = - volume_type_id || - (this.volumeTypes.find((it) => it.label === volume_type) || {}).value; - const volumeTypeItem = this.volumeTypes.find( - (it) => it.value === volumeTypeId - ); - const instanceSnapshotDisk = volumeDetail - ? { - type: volumeTypeId, - typeOption: volumeTypeItem, - size: snapshotSize, - deleteType, - deleteTypeLabel, - } - : null; + const bdmFormatData = JSON.parse(block_device_mapping) || []; + const systemDiskBdm = bdmFormatData[0] || {}; + const instanceSnapshotDisk = getDiskInfo({ + volumeDetail, + snapshotDetail, + selfBdmData: systemDiskBdm, + }); this.updateFormValue('instanceSnapshotDisk', instanceSnapshotDisk); this.updateContext({ instanceSnapshotDisk, + instanceSnapshotDataVolumes, }); this.setState({ instanceSnapshotDisk, instanceSnapshotMinSize: minSize, + instanceSnapshotDataVolumes, }); }; @@ -426,8 +418,14 @@ export class BaseStep extends Base { return instanceSnapshotDisk || oldDisk; }; - renderSnapshotDisk = () => { - const disk = this.getInstanceSnapshotDisk(); + getSnapshotDataDisks = () => { + const { instanceSnapshotDataVolumes } = this.state; + const { instanceSnapshotDataVolumes: oldSnapshotDataVolumes } = + this.props.context; + return instanceSnapshotDataVolumes || oldSnapshotDataVolumes || []; + }; + + renderInstanceSnapshotDisk = (disk) => { if (disk === null) { return null; } @@ -455,6 +453,28 @@ export class BaseStep extends Base { ); }; + renderSnapshotDisk = () => { + const disk = this.getInstanceSnapshotDisk(); + return this.renderInstanceSnapshotDisk(disk); + }; + + renderSnapshotDataDisk = () => { + const dataDisks = this.getSnapshotDataDisks(); + return ( + <> + {dataDisks?.map((i) => { + const disk = getInstanceSnapshotDataDisk(i); + const id = i?.id || i?.snapshot_id; + return ( +
+ {this.renderInstanceSnapshotDisk(disk)} +
+ ); + })} + + ); + }; + get imageColumns() { return getImageColumns(this); } @@ -533,6 +553,15 @@ export class BaseStep extends Base { ); } + get hideInstanceSnapshotSystemDisk() { + return this.showSystemDisk || this.sourceTypeIsVolume; + } + + get hideInstanceSnapshotDataDisk() { + if (this.hideInstanceSnapshotSystemDisk) return true; + return this.getSnapshotDataDisks().length === 0; + } + getFlavorComponent() { return ; } @@ -669,9 +698,15 @@ export class BaseStep extends Base { { name: 'instanceSnapshotDisk', label: t('System Disk'), - hidden: this.showSystemDisk, + hidden: this.hideInstanceSnapshotSystemDisk, component: this.renderSnapshotDisk(), }, + { + name: 'instanceSnapshotDataDisk', + label: t('Required Data Disk'), + hidden: this.hideInstanceSnapshotDataDisk, + component: this.renderSnapshotDataDisk(), + }, { name: 'dataDisk', label: t('Data Disk'), diff --git a/src/resources/cinder/snapshot.jsx b/src/resources/cinder/snapshot.jsx new file mode 100644 index 00000000..c0e3d4fe --- /dev/null +++ b/src/resources/cinder/snapshot.jsx @@ -0,0 +1,60 @@ +// Copyright 2022 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import globalVolumeTypeStore from 'stores/cinder/volume-type'; +import { toJS } from 'mobx'; + +export const volumeTypes = () => { + return (globalVolumeTypeStore.list.data || []).map((it) => ({ + label: it.name, + value: it.id, + originData: toJS(it), + })); +}; + +export const getDiskInfo = (detail) => { + const { + snapshotDetail: { size = 0 } = {}, + volumeDetail: { volume_type } = {}, + selfBdmData = {}, + } = detail || {}; + const { delete_on_termination } = selfBdmData; + const deleteType = delete_on_termination ? 1 : 0; + const deleteTypeLabel = delete_on_termination + ? t('Deleted with the instance') + : t('Not deleted with the instance'); + const volumeTypeItem = volumeTypes().find((it) => it.label === volume_type); + const diskInfo = { + type: volumeTypeItem?.value, + typeOption: volumeTypeItem, + size, + deleteType, + deleteTypeLabel, + }; + return diskInfo; +}; + +export const getInstanceSnapshotDataDisk = (disk) => { + const { + volumeDetail, + snapshotDetail, + bdmFormatData: dataDiskBdm = {}, + } = disk || {}; + const instanceSnapshotDataDisk = getDiskInfo({ + volumeDetail, + snapshotDetail, + selfBdmData: dataDiskBdm, + }); + return instanceSnapshotDataDisk; +}; diff --git a/src/stores/glance/instance-snapshot.js b/src/stores/glance/instance-snapshot.js index 75f08d87..77d6b25f 100644 --- a/src/stores/glance/instance-snapshot.js +++ b/src/stores/glance/instance-snapshot.js @@ -14,6 +14,7 @@ import client from 'client'; import { isSnapshot } from 'src/resources/glance/image'; +import { cloneDeep } from 'lodash'; import Base from '../base'; export class InstanceSnapshotStore extends Base { @@ -152,6 +153,43 @@ export class InstanceSnapshotStore extends Base { item.instanceDetail = instanceResult.server || {}; return item; } + + async fetchInstanceSnapshotVolumeData({ id }) { + const snapshotDetailInfo = await this.client.show(id); + const instanceSnapshotDetail = await this.detailDidFetch( + snapshotDetailInfo + ); + const { block_device_mapping: bdm = '[]' } = instanceSnapshotDetail; + const bdmFormatData = JSON.parse(bdm) || []; + if (!bdmFormatData?.length) { + return instanceSnapshotDetail; + } + const snapshotsOfDataDisk = bdmFormatData?.filter( + (it) => it.boot_index !== 0 + ); + const snapshotsReqs = snapshotsOfDataDisk.map(async (i) => { + const snapshot = cloneDeep(i); + const { snapshot_id } = i; + const snapshotResult = await client.cinder.snapshots.show(snapshot_id); + const snapshotDetail = snapshotResult?.snapshot || {}; + snapshot.snapshotDetail = snapshotDetail; + snapshot.bdmFormatData = i; + return snapshot; + }); + const snapshotsOfDataDiskRes = await Promise.all(snapshotsReqs); + const volumesReqs = snapshotsOfDataDiskRes.map(async (i) => { + const { volume_id } = i.snapshotDetail; + const volumesResult = await client.cinder.volumes.show(volume_id); + const volumeDetail = volumesResult?.volume || {}; + i.volumeDetail = volumeDetail; + return i; + }); + const instanceSnapshotDataVolumes = await Promise.all(volumesReqs); + return { + ...instanceSnapshotDetail, + instanceSnapshotDataVolumes, + }; + } } const globalInstanceSnapshotStore = new InstanceSnapshotStore();