Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions core/src/main/java/com/cloud/resource/ServerResourceBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,6 @@ public Answer listHostLunDevices(Command command) {
}

try {
if (ListHostLunDeviceCommand.MODE_SINGLE.equals(lunPathMode)) {
return new ListHostLunDeviceAnswer(true, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
}

ListHostLunDeviceAnswer fast = listHostLunDevicesFast(lunPathMode);
if (fast != null && fast.getResult()) {
return fast;
Expand Down Expand Up @@ -1959,11 +1955,11 @@ private boolean hasPartitionRecursive(JSONObject device) {
return false;
}

if ("lvm".equals(deviceType) || deviceName.startsWith("/dev/mapper/")) {
if (deviceName.contains("ceph--") && deviceName.contains("--osd--block--")) {
return false;
}
return true;

if (deviceName.startsWith("/dev/mapper/")
&& deviceName.contains("ceph--")
&& deviceName.contains("--osd--block--")) {
return false;
}

if (device.has("children")) {
Expand All @@ -1972,16 +1968,11 @@ private boolean hasPartitionRecursive(JSONObject device) {
for (int i = 0; i < children.length(); i++) {
JSONObject child = children.getJSONObject(i);
String childType = child.optString("type", "");
String childName = child.optString("name", "");

if ("part".equals(childType)) {
return true;
}

if ("lvm".equals(childType)) {
return true;
}

if (hasPartitionRecursive(child)) {
return true;
}
Expand Down Expand Up @@ -4082,15 +4073,17 @@ private String validateLunDeviceForAttachment(String devicePath, String vmName)
return "파티션 디바이스는 할당할 수 없습니다. 전체 디스크를 사용해주세요: " + devicePath;
}

if (isLunDeviceAllocatedToOtherVm(devicePath, vmName)) {
return "디바이스가 이미 다른 VM에 할당되어 있습니다: " + devicePath;
String allocatedVmName = findLunDeviceAllocatedVmName(devicePath, vmName);
if (allocatedVmName != null) {
return "디바이스가 이미 다른 VM에 할당되어 있습니다: " + devicePath + " (VM: " + allocatedVmName + ")";
}

Map<String, DeviceMapping> mappings = buildDeviceMapping();
DeviceMapping mapping = findMappedDevice(devicePath, mappings);
if (mapping != null && mapping.getScsiDevicePath() != null) {
if (isLunDeviceAllocatedToOtherVm(mapping.getScsiDevicePath(), vmName)) {
return "매핑된 SCSI 디바이스가 이미 다른 VM에 할당되어 있습니다: " + mapping.getScsiDevicePath();
String mappedAllocatedVmName = findLunDeviceAllocatedVmName(mapping.getScsiDevicePath(), vmName);
if (mappedAllocatedVmName != null) {
return "매핑된 SCSI 디바이스가 이미 다른 VM에 할당되어 있습니다: " + mapping.getScsiDevicePath() + " (VM: " + mappedAllocatedVmName + ")";
}
}

Expand Down Expand Up @@ -4185,29 +4178,41 @@ private boolean isPartitionDevice(String devicePath) {
}

private boolean isLunDeviceAllocatedToOtherVm(String devicePath, String currentVmName) {
return findLunDeviceAllocatedVmName(devicePath, currentVmName) != null;
}

private String findLunDeviceAllocatedVmName(String devicePath, String currentVmName) {
try {
Script listCommand = new Script("/bin/bash");
listCommand.add("-c");
listCommand.add("virsh list --all | grep -v 'Id' | grep -v '^-' | while read line; do " +
"vm_id=$(echo $line | awk '{print $1}'); " +
"if [ ! -z \"$vm_id\" ] && [ \"$vm_id\" != \"" + currentVmName + "\" ]; then " +
"virsh dumpxml $vm_id | grep -q '" + devicePath + "'; " +
listCommand.add("virsh list --all --name | while read vm_name; do " +
"if [ -z \"$vm_name\" ] || [ \"$vm_name\" = \"" + currentVmName + "\" ]; then " +
"continue; " +
"fi; " +
"virsh dumpxml \"$vm_name\" | grep -Fq \"dev='" + devicePath + "'\"; " +
"if [ $? -ne 0 ]; then " +
"virsh dumpxml \"$vm_name\" | grep -Fq 'dev=\"" + devicePath + "\"'; " +
"fi; " +
"if [ $? -eq 0 ]; then " +
"echo 'allocated'; " +
"echo \"$vm_name\"; " +
"break; " +
"fi; " +
"fi; " +
"done");
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
String result = listCommand.execute(parser);

if (result == null && parser.getLines() != null) {
return parser.getLines().trim().equals("allocated");
String[] lines = parser.getLines().trim().split("\\r?\\n");
if (lines.length >= 2 && "allocated".equals(lines[0].trim())) {
String vmName = lines[1].trim();
return vmName.isEmpty() ? "unknown" : vmName;
}
}
return false;
return null;
} catch (Exception e) {
logger.debug("Error checking device allocation to other VMs: {}", e.getMessage());
return false;
return null;
}
}

Expand Down
56 changes: 54 additions & 2 deletions ui/src/views/compute/InstanceTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ export default {

formattedText = formattedText.replace(/(Revision:\s+[^\s\n]+)( +)(?=\S)/gi, '$1\n$2')

formattedText = formattedText.replace(/HAS_PARTITIONS:\s*false/gi, this.$t('label.no.partitions'))
formattedText = formattedText.replace(/HAS_PARTITIONS:\s*false/gi, '')
formattedText = formattedText.replace(/HAS_PARTITIONS:\s*true/gi, this.$t('label.has.partitions'))

formattedText = formattedText.replace(/USAGE_STATUS:\s*사용안함/gi, '사용안함')
Expand Down Expand Up @@ -724,6 +724,21 @@ export default {
}
})

const hostId = this.vm?.hostid || categorized.lun?.[0]?.hostId
if (hostId && categorized.lun.length > 0) {
const lunDetailMap = await this.fetchLunDetailMap(hostId)
categorized.lun = categorized.lun.map(device => {
if (device.hostDevicesText && String(device.hostDevicesText).trim().length > 0) {
return device
}
const detail = lunDetailMap[device.hostDevicesName]
if (detail) {
return { ...device, hostDevicesText: detail }
}
return device
})
}

this.pciDevices = categorized.pci
this.usbDevices = categorized.usb
this.lunDevices = categorized.lun
Expand All @@ -747,6 +762,44 @@ export default {

return this.deviceAssignmentsPromise
},
async fetchLunDetailMap (hostId) {
const detailMap = {}
try {
const [singleSettled, multiSettled] = await Promise.allSettled([
api('listHostLunDevices', { id: hostId, lunpathmode: 'single', lunPathMode: 'single' }),
api('listHostLunDevices', { id: hostId, lunpathmode: 'multipath', lunPathMode: 'multipath' })
])

const sources = []
if (singleSettled.status === 'fulfilled') {
sources.push(singleSettled.value?.listhostlundevicesresponse?.listhostlundevices?.[0])
}
if (multiSettled.status === 'fulfilled') {
sources.push(multiSettled.value?.listhostlundevicesresponse?.listhostlundevices?.[0])
}

sources.forEach(src => {
if (!src) return
if (src.devicedetails && typeof src.devicedetails === 'object') {
Object.entries(src.devicedetails).forEach(([name, text]) => {
if (name && text && !detailMap[name]) {
detailMap[name] = text
}
})
}
const names = Array.isArray(src.hostdevicesname) ? src.hostdevicesname : []
const texts = Array.isArray(src.hostdevicestext) ? src.hostdevicestext : []
names.forEach((name, idx) => {
const text = texts[idx]
if (name && text && !detailMap[name]) {
detailMap[name] = text
}
})
})
} catch (e) {
}
return detailMap
},
async fetchPciDevices () {
await this.loadDevicesFromDb()
},
Expand Down Expand Up @@ -893,7 +946,6 @@ export default {

}
</style>

<style scoped>
.wide-modal {
min-width: 50vw;
Expand Down
Loading
Loading