diff --git a/header/src/main/java/org/zstack/header/storage/addon/IscsiRemoteTarget.java b/header/src/main/java/org/zstack/header/storage/addon/IscsiRemoteTarget.java index fa73f3579b..99edc84c56 100644 --- a/header/src/main/java/org/zstack/header/storage/addon/IscsiRemoteTarget.java +++ b/header/src/main/java/org/zstack/header/storage/addon/IscsiRemoteTarget.java @@ -1,6 +1,12 @@ package org.zstack.header.storage.addon; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import java.net.URI; + public class IscsiRemoteTarget extends BlockRemoteTarget { + private final static CLogger logger = Utils.getLogger(IscsiRemoteTarget.class); private String transport = "tcp"; private String iqn; @@ -70,4 +76,50 @@ public enum DiskIdType { wwn, serial } + + public static IscsiRemoteTarget fromUri(String uriString) { + try { + URI uri = URI.create(uriString); + + if (!"iscsi".equalsIgnoreCase(uri.getScheme())) { + logger.info("Invalid URI scheme. Expected 'iscsi', got: " + uri.getScheme()); + return null; + } + + IscsiRemoteTarget target = new IscsiRemoteTarget(); + String authority = uri.getAuthority(); + if (authority == null || authority.isEmpty()) { + logger.info("Invalid URI authority: " + uri.getAuthority()); + return null; + } + String[] serverHostNames = authority.split(":")[0].split(","); + target.setIp(serverHostNames[0]); + target.setPort(uri.getPort()); + + // parse: /{iqn}/{diskIdType}_{diskId} + String path = uri.getPath(); + if (path != null && path.startsWith("/")) { + String[] pathParts = path.substring(1).split("/"); + if (pathParts.length >= 2) { + target.setIqn(pathParts[0]); + String[] diskParts = pathParts[1].split("_", 2); + if (diskParts.length == 2) { + target.setDiskIdType(diskParts[0]); + target.setDiskId(diskParts[1]); + } else { + logger.info("Invalid diskId format in URI path: " + pathParts[1]); + return null; + } + } else { + logger.info("Invalid URI path format: " + path); + return null; + } + } + + return target; + } catch (Exception e) { + logger.error("Failed to parse URI: " + uriString, e); + return null; + } + } } diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java b/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java index 5d6896960c..5291fd7a0e 100644 --- a/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java @@ -54,4 +54,7 @@ public interface PrimaryStorageControllerSvc { void onFirstAdditionConfigure(Completion completion); long alignSize(long size); + + String getVolumeLunId(String volInstallPath); + } diff --git a/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java b/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java index 7e6619497a..a448c0b091 100644 --- a/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java +++ b/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java @@ -37,10 +37,7 @@ import org.zstack.header.storage.primary.ImageCacheInventory; import org.zstack.header.storage.primary.VolumeSnapshotCapability; import org.zstack.header.storage.snapshot.VolumeSnapshotStats; -import org.zstack.header.volume.VolumeConstant; -import org.zstack.header.volume.VolumeInventory; -import org.zstack.header.volume.VolumeProtocol; -import org.zstack.header.volume.VolumeStats; +import org.zstack.header.volume.*; import org.zstack.iscsi.IscsiUtils; import org.zstack.iscsi.kvm.IscsiHeartbeatVolumeTO; import org.zstack.iscsi.kvm.IscsiVolumeTO; @@ -63,6 +60,7 @@ import static org.zstack.core.Platform.operr; import static org.zstack.expon.ExponIscsiHelper.*; import static org.zstack.expon.ExponNameHelper.*; +import static org.zstack.iscsi.IscsiUtils.getGatewayMnIpFromInitiatorName; import static org.zstack.iscsi.IscsiUtils.getHostMnIpFromInitiatorName; import static org.zstack.storage.addon.primary.ExternalPrimaryStorageNameHelper.*; @@ -239,6 +237,10 @@ public List getIscsiServers(String tianshuId) { private synchronized ActiveVolumeTO activeIscsiVolume(HostInventory h, BaseVolumeInfo vol, boolean shareable) { String clientIqn = IscsiUtils.getHostInitiatorName(h.getUuid()); + if (h.getHypervisorType().equals("baremetal2")) { + VolumeVO volume = dbf.findByUuid(vol.getUuid(), VolumeVO.class); + clientIqn = String.format("iqn.2015-01.io.zstack:initiator.instance.%s", volume.getVmInstanceUuid()); + } if (clientIqn == null) { throw new RuntimeException(String.format("cannot get host[uuid:%s] initiator name", h.getUuid())); } @@ -529,7 +531,7 @@ public List getActiveClients(String installPath, String prot ActiveVolumeClient c = new ActiveVolumeClient(); if (it.contains("iqn")) { c.setQualifiedName(it); - c.setManagerIp(getHostMnIpFromInitiatorName(it)); + c.setManagerIp(getHostMnIpFromInitiatorName(it) != null ? getHostMnIpFromInitiatorName(it) : getGatewayMnIpFromInitiatorName(it)); } else { c.setManagerIp(it); } @@ -579,12 +581,16 @@ public void deactivate(String installPath, String protocol, HostInventory h, Com @Override public void deactivate(String installPath, String protocol, ActiveVolumeClient client, Completion comp) { HostVO host = Q.New(HostVO.class).eq(HostVO_.managementIp, client.getManagerIp()).find(); - if (host != null) { - deactivate(installPath, protocol, HostInventory.valueOf(host), comp); - } else { + if (host == null) { + comp.fail(operr("deactivate fail, cannot find host[ip:%s]", client.getManagerIp())); + return; + } + if (host.getHypervisorType().equals("baremetal2")) { // bm instance InitiatorName deactivateIscsi(installPath, client.getQualifiedName()); comp.success(); + } else { + deactivate(installPath, protocol, HostInventory.valueOf(host), comp); } } @@ -1327,6 +1333,11 @@ public long alignSize(long size) { return size; } + @Override + public String getVolumeLunId(String volInstallPath) { + return apiHelper.getVolumeLunDetail(getVolIdFromPath(volInstallPath)).getLunId(); + } + private void retry(Runnable r) { retry(r, 3); } diff --git a/plugin/iscsi/src/main/java/org/zstack/iscsi/IscsiUtils.java b/plugin/iscsi/src/main/java/org/zstack/iscsi/IscsiUtils.java index 034ad33f20..c6cd72bda5 100644 --- a/plugin/iscsi/src/main/java/org/zstack/iscsi/IscsiUtils.java +++ b/plugin/iscsi/src/main/java/org/zstack/iscsi/IscsiUtils.java @@ -11,6 +11,8 @@ import org.zstack.header.storage.backup.*; import org.zstack.header.tag.SystemTagVO; import org.zstack.header.tag.SystemTagVO_; +import org.zstack.header.vm.VmInstanceVO; +import org.zstack.header.vm.VmInstanceVO_; import org.zstack.storage.backup.BackupStorageSystemTags; import java.util.Collections; @@ -38,6 +40,27 @@ public static String getHostMnIpFromInitiatorName(String initiatorName) { return null; } + public static String getGatewayMnIpFromInitiatorName(String initiatorName) { + String requiredPrefix = "iqn.2015-01.io.zstack:initiator.instance."; + + if (initiatorName == null || initiatorName.isEmpty()) { + return null; + } + + if (!initiatorName.startsWith(requiredPrefix)) { + return null; + } + + String uuid = initiatorName.substring(requiredPrefix.length()); + String hostUuid = Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, uuid) + .select(VmInstanceVO_.hostUuid) + .findValue(); + return Q.New(HostVO.class).eq(HostVO_.uuid, hostUuid) + .eq(HostVO_.hypervisorType, "baremetal2") + .select(HostVO_.managementIp) + .findValue(); + } + private static String getBsMnIp(String bsUuid) { CloudBus bus = Platform.getComponentLoader().getComponent(CloudBus.class); GetBackupStorageManagerHostnameMsg msg = new GetBackupStorageManagerHostnameMsg(); diff --git a/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java b/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java index 67b5d3904a..a5f6fe7c53 100644 --- a/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java +++ b/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java @@ -1030,6 +1030,11 @@ public long alignSize(long size) { return SizeUnit.MEGABYTE.toByte(convertBytesToMegaBytes(size)); } + @Override + public String getVolumeLunId(String volInstallPath) { + return null; + } + public void cleanActiveRecord(VolumeInventory vol) { } diff --git a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java index 2ae9b36512..40fe84b276 100644 --- a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java +++ b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java @@ -1024,6 +1024,11 @@ public long alignSize(long size) { return convertSizeToByte(alignSizeTo(size, unit), unit); } + @Override + public String getVolumeLunId(String volInstallPath) { + return null; + } + public void doDeleteVolume(String installPath, Boolean force, Completion comp) { DeleteVolumeCmd cmd = new DeleteVolumeCmd(); cmd.setPath(installPath); diff --git a/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java b/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java index cfc8e3db3c..2fb6c8f009 100644 --- a/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java +++ b/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java @@ -455,6 +455,10 @@ private void activeVolumeIfNeed(VmInstanceInventory vm, VolumeInventory volume, } HostInventory host = HostInventory.valueOf(dbf.findByUuid(vm.getHostUuid(), HostVO.class)); + if (host.getHypervisorType().equals("baremetal2")) { + completion.success(); + return; + } svc.activate(BaseVolumeInfo.valueOf(volume), host, volume.isShareable(), new ReturnValueCompletion(completion) { @Override public void success(ActiveVolumeTO returnValue) {