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