feat: support create non-bfv instance
support create non-bfv instance Closes-Bug: #2003057 Change-Id: If494648f4d42c6da54661400283968c7220bf2a4
This commit is contained in:
@@ -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.
|
@@ -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'),
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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') {
|
||||||
|
Reference in New Issue
Block a user