diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java index 18770b210b7..87ecc5700e4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java @@ -48,9 +48,10 @@ public class AttachVolumeCmd extends BaseAsyncCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.LONG, description = "the ID of the device to map the volume to within the guest OS. " - + "If no deviceId is passed in, the next available deviceId will be chosen. " + "Possible values for a Linux OS are:" + "* 0 - /dev/xvda" + "* 1 - /dev/xvdb" + "* 2 - /dev/xvdc" - + "* 4 - /dev/xvde" + "* 5 - /dev/xvdf" + "* 6 - /dev/xvdg" + "* 7 - /dev/xvdh" + "* 8 - /dev/xvdi" + "* 9 - /dev/xvdj") + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.LONG, description = "The ID of the device to map the volume to the guest OS. " + + "If no deviceID is informed, the next available deviceID will be chosen. When using a linux operating system and the hypervisor XenServer, the devices IDs will be mapped as follows:" + + "" + + "Please refer to the docs of your hypervisor for the correct mapping of the deviceID and the actual logical disk structure.") private Long deviceId; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the disk volume") diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index c5990dae98f..338dc14b43d 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -107,7 +107,7 @@ export default { icon: 'paper-clip-outlined', label: 'label.action.attach.disk', dataView: true, - show: (record) => { return record.type !== 'ROOT' && ['Allocated', 'Ready', 'Uploaded'].includes(record.state) && !('virtualmachineid' in record) }, + show: (record) => { return ['Allocated', 'Ready', 'Uploaded'].includes(record.state) && !('virtualmachineid' in record) }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/AttachVolume.vue'))) }, @@ -117,10 +117,7 @@ export default { label: 'label.action.detach.disk', message: 'message.detach.disk', dataView: true, - show: (record) => { - return record.type !== 'ROOT' && record.virtualmachineid && - ['Running', 'Stopped', 'Destroyed'].includes(record.vmstate) - } + show: (record) => { return record.virtualmachineid && ['Running', 'Stopped', 'Destroyed'].includes(record.vmstate) } }, { api: 'updateVolume', diff --git a/ui/src/views/storage/AttachVolume.vue b/ui/src/views/storage/AttachVolume.vue index 080911496b6..96e81ba38e9 100644 --- a/ui/src/views/storage/AttachVolume.vue +++ b/ui/src/views/storage/AttachVolume.vue @@ -47,6 +47,25 @@ {{ vm.name || vm.displayname }} + + +
+ + + + + + + +
+
@@ -85,7 +104,8 @@ export default { this.formRef = ref() this.form = reactive({}) this.rules = reactive({ - virtualmachineid: [{ required: true, message: this.$t('message.error.select') }] + virtualmachineid: [{ required: true, message: this.$t('message.error.select') }], + deviceid: [{ required: true, message: this.$t('message.error.select') }] }) }, fetchData () { @@ -127,7 +147,8 @@ export default { this.loading = true api('attachVolume', { id: this.resource.id, - virtualmachineid: values.virtualmachineid + virtualmachineid: values.virtualmachineid, + deviceid: values.deviceid }).then(response => { this.$pollJob({ jobId: response.attachvolumeresponse.jobid,