feat: support create non-bfv instance

support create non-bfv instance

Closes-Bug: #2003057
Change-Id: If494648f4d42c6da54661400283968c7220bf2a4
This commit is contained in:
Jingwei.Zhang
2023-09-28 17:18:39 +08:00
parent 12bc328c24
commit 6a186ee521
4 changed files with 145 additions and 27 deletions

View File

@@ -0,0 +1,10 @@
---
fixes:
- |
`Feature #2003057 <https://bugs.launchpad.net/skyline-apiserver/+bug/2003057>`_:
Support create No-BFV instance:
* Support no-boot-from-volume selector when create instance by image or instance snapshot.
* When create a no-boot-from-volume instance, you can not add data disk in the step forms, but you can attach volumes when the instance has been created.

View File

@@ -82,6 +82,9 @@ export class BaseStep extends Base {
project: this.currentProjectName, project: this.currentProjectName,
dataDisk: [], dataDisk: [],
}; };
if (source.value === 'image') {
values.bootFromVolume = true;
}
return values; return values;
} }
@@ -272,7 +275,14 @@ export class BaseStep extends Base {
}; };
get nameForStateUpdate() { get nameForStateUpdate() {
return ['source', 'image', 'instanceSnapshot', 'bootableVolume', 'flavor']; return [
'source',
'image',
'instanceSnapshot',
'bootableVolume',
'flavor',
'bootFromVolume',
];
} }
getSystemDiskMinSize() { getSystemDiskMinSize() {
@@ -333,6 +343,17 @@ export class BaseStep extends Base {
}); });
}; };
onChangeBootFromVolume = (value) => {
const newData = {
bootFromVolume: value,
};
if (!value) {
newData.dataDisk = [];
this.updateFormValue('dataDisk', []);
}
this.updateContext(newData);
};
onInstanceSnapshotChange = async (value) => { onInstanceSnapshotChange = async (value) => {
const { min_disk, size, id } = value.selectedRows[0] || {}; const { min_disk, size, id } = value.selectedRows[0] || {};
if (!id) { if (!id) {
@@ -357,14 +378,17 @@ export class BaseStep extends Base {
instanceSnapshotDataVolumes = [], instanceSnapshotDataVolumes = [],
} = detail; } = detail;
if (!volumeDetail) { if (!volumeDetail) {
this.updateFormValue('bootFromVolume', true);
this.updateContext({ this.updateContext({
instanceSnapshotDisk: null, instanceSnapshotDisk: null,
instanceSnapshotDataVolumes: [], instanceSnapshotDataVolumes: [],
bootFromVolume: true,
}); });
this.setState({ this.setState({
instanceSnapshotDisk: null, instanceSnapshotDisk: null,
instanceSnapshotMinSize: 0, instanceSnapshotMinSize: 0,
instanceSnapshotDataVolumes: [], instanceSnapshotDataVolumes: [],
bootFromVolume: true,
}); });
} }
const minSize = Math.max(min_disk, size, snapshotSize); const minSize = Math.max(min_disk, size, snapshotSize);
@@ -544,6 +568,36 @@ export class BaseStep extends Base {
]; ];
} }
get supportNoBootFromVolume() {
return true;
}
get showBootFromVolumeFormItem() {
if (!this.supportNoBootFromVolume) {
return false;
}
if (!this.enableCinder) {
return false;
}
if (this.sourceTypeIsImage) {
return true;
}
return this.showSystemDisk;
}
get bootFromVolumeOptions() {
return [
{
value: true,
label: t('Yes - Create a new system disk'),
},
{
value: false,
label: t('No - Do not create a new system disk'),
},
];
}
get showSystemDisk() { get showSystemDisk() {
const snapshotDisk = this.getInstanceSnapshotDisk(); const snapshotDisk = this.getInstanceSnapshotDisk();
return ( return (
@@ -553,6 +607,18 @@ export class BaseStep extends Base {
); );
} }
get showSystemDiskByBootFromVolume() {
if (!this.showSystemDisk) {
return false;
}
if (!this.supportNoBootFromVolume) {
return true;
}
// support non-bfv and bootFromVolume = true
const { bootFromVolume = true } = this.state;
return !!bootFromVolume;
}
get hideInstanceSnapshotSystemDisk() { get hideInstanceSnapshotSystemDisk() {
return this.showSystemDisk || this.sourceTypeIsVolume; return this.showSystemDisk || this.sourceTypeIsVolume;
} }
@@ -562,6 +628,17 @@ export class BaseStep extends Base {
return this.getSnapshotDataDisks().length === 0; return this.getSnapshotDataDisks().length === 0;
} }
get hideDataDisk() {
if (!this.supportNoBootFromVolume) {
return false;
}
if (this.sourceTypeIsVolume) {
return false;
}
const { bootFromVolume = true } = this.state;
return !bootFromVolume;
}
getFlavorComponent() { getFlavorComponent() {
return <FlavorSelectTable onChange={this.onFlavorChange} />; return <FlavorSelectTable onChange={this.onFlavorChange} />;
} }
@@ -683,13 +760,30 @@ export class BaseStep extends Base {
{ {
type: 'divider', type: 'divider',
}, },
{
name: 'bootFromVolume',
label: t('Boot From Volume'),
type: 'radio',
required: this.showBootFromVolumeFormItem,
hidden: !this.showBootFromVolumeFormItem,
onChange: this.onChangeBootFromVolume,
wrapperCol: {
xs: {
span: 16,
},
sm: {
span: 14,
},
},
options: this.bootFromVolumeOptions,
},
{ {
name: 'systemDisk', name: 'systemDisk',
label: t('System Disk'), label: t('System Disk'),
type: 'instance-volume', type: 'instance-volume',
options: this.volumeTypes, options: this.volumeTypes,
required: this.showSystemDisk, required: this.showSystemDiskByBootFromVolume,
hidden: !this.showSystemDisk, hidden: !this.showSystemDiskByBootFromVolume,
validator: this.checkSystemDisk, validator: this.checkSystemDisk,
minSize: this.getSystemDiskMinSize(), minSize: this.getSystemDiskMinSize(),
extra: t('Disk size is limited by the min disk of flavor, image, etc.'), extra: t('Disk size is limited by the min disk of flavor, image, etc.'),
@@ -713,6 +807,7 @@ export class BaseStep extends Base {
type: 'add-select', type: 'add-select',
options: this.volumeTypes, options: this.volumeTypes,
defaultItemValue: this.defaultVolumeType, defaultItemValue: this.defaultVolumeType,
hidden: this.hideDataDisk,
itemComponent: InstanceVolume, itemComponent: InstanceVolume,
minCount: 0, minCount: 0,
addTextTips: t('Data Disks'), addTextTips: t('Data Disks'),

View File

@@ -36,7 +36,10 @@ export class ConfirmStep extends Base {
allowed = () => Promise.resolve(); allowed = () => Promise.resolve();
getDisk(diskInfo) { getDisk(diskInfo, bootFromVolume) {
if (!bootFromVolume) {
return null;
}
const { size, typeOption, deleteTypeLabel } = diskInfo || {}; const { size, typeOption, deleteTypeLabel } = diskInfo || {};
return `${typeOption.label} ${size}GiB ${deleteTypeLabel}`; return `${typeOption.label} ${size}GiB ${deleteTypeLabel}`;
} }
@@ -54,14 +57,15 @@ export class ConfirmStep extends Base {
systemDisk, systemDisk,
source: { value } = {}, source: { value } = {},
instanceSnapshotDisk, instanceSnapshotDisk,
bootFromVolume = true,
} = context; } = context;
if (value === 'bootableVolume') { if (value === 'bootableVolume') {
return this.getBootableVolumeDisk(); return this.getBootableVolumeDisk();
} }
if (value === 'instanceSnapshot' && instanceSnapshotDisk !== null) { if (value === 'instanceSnapshot' && instanceSnapshotDisk !== null) {
return this.getDisk(instanceSnapshotDisk); return this.getDisk(instanceSnapshotDisk, bootFromVolume);
} }
return this.getDisk(systemDisk); return this.getDisk(systemDisk, bootFromVolume);
} }
getDataDisk() { getDataDisk() {
@@ -79,7 +83,7 @@ export class ConfirmStep extends Base {
) { ) {
allDataDisks = getAllDataDisks({ dataDisk, instanceSnapshotDataVolumes }); allDataDisks = getAllDataDisks({ dataDisk, instanceSnapshotDataVolumes });
} }
return allDataDisks.map((it) => this.getDisk(it.value)); return allDataDisks.map((it) => this.getDisk(it.value, true));
} }
getFlavor() { getFlavor() {

View File

@@ -301,6 +301,7 @@ export class StepCreate extends StepAction {
source: { value: sourceValue } = {}, source: { value: sourceValue } = {},
instanceSnapshotDisk = {}, instanceSnapshotDisk = {},
instanceSnapshotDataVolumes = [], instanceSnapshotDataVolumes = [],
bootFromVolume = true,
} = data; } = data;
const newCountMap = {}; const newCountMap = {};
const newSizeMap = {}; const newSizeMap = {};
@@ -309,7 +310,7 @@ export class StepCreate extends StepAction {
const isSnapshotType = sourceValue === 'instanceSnapshot'; const isSnapshotType = sourceValue === 'instanceSnapshot';
if (isSnapshotType && instanceSnapshotDisk) { if (isSnapshotType && instanceSnapshotDisk) {
const { size, typeOption: { label } = {} } = instanceSnapshotDisk; const { size, typeOption: { label } = {} } = instanceSnapshotDisk;
if (label) { if (label && bootFromVolume) {
newCountMap[label] = !newCountMap[label] ? 1 : newCountMap[label] + 1; newCountMap[label] = !newCountMap[label] ? 1 : newCountMap[label] + 1;
newSizeMap[label] = !newSizeMap[label] newSizeMap[label] = !newSizeMap[label]
? size ? size
@@ -317,7 +318,7 @@ export class StepCreate extends StepAction {
totalNewCount += 1 * count; totalNewCount += 1 * count;
totalNewSize += size * count; totalNewSize += size * count;
} }
} else if (systemDisk.type) { } else if (systemDisk.type && bootFromVolume) {
const { size } = systemDisk; const { size } = systemDisk;
const { label } = systemDisk.typeOption || {}; const { label } = systemDisk.typeOption || {};
newCountMap[label] = !newCountMap[label] ? 1 : newCountMap[label] + 1; newCountMap[label] = !newCountMap[label] ? 1 : newCountMap[label] + 1;
@@ -577,6 +578,7 @@ export class StepCreate extends StepAction {
instanceSnapshotDisk, instanceSnapshotDisk,
source, source,
systemDisk, systemDisk,
bootFromVolume = true,
} = values; } = values;
const { value: sourceValue } = source; const { value: sourceValue } = source;
const imageRef = const imageRef =
@@ -592,21 +594,23 @@ export class StepCreate extends StepAction {
} }
let rootVolume = {}; let rootVolume = {};
if (sourceValue !== 'bootableVolume') { if (sourceValue !== 'bootableVolume') {
const { deleteType, type, size } = systemDisk || {}; if (bootFromVolume) {
rootVolume = { const { deleteType, type, size } = systemDisk || {};
boot_index: 0, rootVolume = {
uuid: imageRef, boot_index: 0,
source_type: 'image', uuid: imageRef,
volume_size: size, source_type: 'image',
destination_type: 'volume', volume_size: size,
volume_type: type, destination_type: 'volume',
delete_on_termination: deleteType === 1, volume_type: type,
}; delete_on_termination: deleteType === 1,
if (sourceValue === 'instanceSnapshot') { };
if (instanceSnapshotDisk) { if (sourceValue === 'instanceSnapshot') {
delete rootVolume.volume_size; if (instanceSnapshotDisk) {
delete rootVolume.volume_type; delete rootVolume.volume_size;
delete rootVolume.delete_on_termination; delete rootVolume.volume_type;
delete rootVolume.delete_on_termination;
}
} }
} }
} else { } else {
@@ -636,15 +640,19 @@ export class StepCreate extends StepAction {
if ( if (
sourceValue === 'image' && sourceValue === 'image' &&
image.selectedRows[0].disk_format === 'iso' && image.selectedRows[0].disk_format === 'iso' &&
dataVolumes[0] dataVolumes[0] &&
bootFromVolume
) { ) {
dataVolumes[0].boot_index = 0; dataVolumes[0].boot_index = 0;
dataVolumes[0].device_type = 'disk'; dataVolumes[0].device_type = 'disk';
rootVolume.boot_index = 1; rootVolume.boot_index = 1;
rootVolume.device_type = 'cdrom'; rootVolume.device_type = 'cdrom';
} }
const volumes = isEmpty(rootVolume)
? [...dataVolumes]
: [rootVolume, ...dataVolumes];
return { return {
volumes: [rootVolume, ...dataVolumes], volumes,
imageRef, imageRef,
}; };
} }
@@ -695,6 +703,7 @@ export class StepCreate extends StepAction {
serverGroup, serverGroup,
name, name,
count = 1, count = 1,
bootFromVolume = true,
} = values; } = values;
if (hasIp && count > 1) { if (hasIp && count > 1) {
this.ipBatchError = true; this.ipBatchError = true;
@@ -714,7 +723,7 @@ export class StepCreate extends StepAction {
if (this.enableCinder) { if (this.enableCinder) {
server.block_device_mapping_v2 = volumes; server.block_device_mapping_v2 = volumes;
} }
if (imageRef && !volumes) { if (imageRef && (!volumes || !bootFromVolume)) {
server.imageRef = imageRef; server.imageRef = imageRef;
} }
if (loginType.value === 'keypair') { if (loginType.value === 'keypair') {