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
This commit is contained in:
zhangke 2022-10-13 15:13:42 +08:00
parent 27fb2b2db0
commit fdd6dbb5e3
5 changed files with 170 additions and 35 deletions

View File

@ -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",

View File

@ -1940,6 +1940,7 @@
"Request ID": "请求ID",
"Require": "强制",
"Require(Need multithreading)": "Require必须有多线程",
"Required Data Disk": "所需数据盘",
"Rescue": "救援",
"Rescued": "已救援",
"Rescuing": "救援中",

View File

@ -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 (
<div style={{ marginBottom: 10 }} key={`data-disk-${id}`}>
{this.renderInstanceSnapshotDisk(disk)}
</div>
);
})}
</>
);
};
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 <FlavorSelectTable onChange={this.onFlavorChange} />;
}
@ -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'),

View File

@ -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;
};

View File

@ -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();