diff --git a/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java b/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java index 3db3e863c5c..43ce4a6ac8a 100644 --- a/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java +++ b/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java @@ -24,6 +24,7 @@ import com.cloud.agent.api.Command; public class MigrateVolumeAnswer extends Answer { private String volumePath; + private String volumeChain; public MigrateVolumeAnswer(Command command, boolean success, String details, String volumePath) { super(command, success, details); @@ -38,4 +39,12 @@ public class MigrateVolumeAnswer extends Answer { public String getVolumePath() { return volumePath; } + + public String getVolumeChainInfo() { + return volumeChain; + } + + public void setVolumeChainInfo(String volumeChain) { + this.volumeChain = volumeChain; + } } \ No newline at end of file diff --git a/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java b/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java index 80736891cb1..16452afe63c 100644 --- a/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java +++ b/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java @@ -22,8 +22,11 @@ package org.apache.cloudstack.storage.command; import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DiskTO; +import java.util.Map; + public class AttachAnswer extends Answer { private DiskTO disk; + private Map diskDetails; public AttachAnswer() { super(null); @@ -34,6 +37,12 @@ public class AttachAnswer extends Answer { setDisk(disk); } + public AttachAnswer(DiskTO disk, Map diskDetails) { + super(null); + setDisk(disk); + setDiskDetails(diskDetails); + } + public AttachAnswer(String errMsg) { super(null, false, errMsg); } @@ -45,4 +54,12 @@ public class AttachAnswer extends Answer { public void setDisk(DiskTO disk) { this.disk = disk; } + + public Map getDiskDetails() { + return diskDetails; + } + + public void setDiskDetails(Map diskDetails) { + this.diskDetails = diskDetails; + } } diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 7e572c3c505..c9c35b45d7a 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -407,6 +407,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { VolumeVO volumeVo = volDao.findById(volume.getId()); Long oldPoolId = volume.getPoolId(); volumeVo.setPath(((MigrateVolumeAnswer)answer).getVolumePath()); + String chainInfo = ((MigrateVolumeAnswer) answer).getVolumeChainInfo(); + if (chainInfo != null) { + volumeVo.setChainInfo(chainInfo); + } volumeVo.setPodId(destPool.getPodId()); volumeVo.setPoolId(destPool.getId()); volumeVo.setLastPoolId(oldPoolId); diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java index da9764de54d..4b77aab6170 100644 --- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java @@ -207,6 +207,9 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy { VolumeVO volumeVO = volDao.findById(volume.getId()); Long oldPoolId = volumeVO.getPoolId(); volumeVO.setPath(volumeTo.getPath()); + if (volumeTo.getChainInfo() != null) { + volumeVO.setChainInfo(volumeTo.getChainInfo()); + } volumeVO.setLastPoolId(oldPoolId); volumeVO.setFolder(pool.getPath()); volumeVO.setPodId(pool.getPodId()); diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index fa2888ab3f5..be143166dd6 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -26,6 +26,7 @@ import java.util.concurrent.ExecutionException; import javax.inject.Inject; +import org.apache.cloudstack.utils.volume.VirtualMachineVolumeChainInfo; import org.apache.log4j.Logger; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; @@ -96,16 +97,19 @@ import com.cloud.exception.StorageUnavailableException; import com.cloud.gpu.GPU; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorCapabilitiesVO; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.org.Grouping; +import com.cloud.serializer.GsonHelper; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.storage.snapshot.SnapshotApiService; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.TemplateManager; @@ -134,6 +138,7 @@ import com.cloud.utils.db.UUIDManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -154,6 +159,8 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import com.google.gson.Gson; +import com.google.gson.JsonParseException; public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService, VmWorkJobHandler { private final static Logger s_logger = Logger.getLogger(VolumeApiServiceImpl.class); @@ -176,6 +183,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject VolumeDao _volsDao; @Inject + VolumeDetailsDao _volDetailDao; + @Inject HostDao _hostDao; @Inject SnapshotDao _snapshotDao; @@ -225,6 +234,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic VmWorkJobDao _workJobDao; @Inject ClusterDetailsDao _clusterDetailsDao; + @Inject + UserVmManager _userVmMgr; + protected Gson _gson; private List _storagePoolAllocators; @@ -238,6 +250,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic protected VolumeApiServiceImpl() { _volStateMachine = Volume.State.getStateMachine(); + _gson = GsonHelper.getGsonLogger(); } /* @@ -1781,6 +1794,23 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Cannot migrate ROOT volume of a stopped VM to a storage pool in a different VMware datacenter"); } } + + String rootVolChainInfo = vol.getChainInfo(); + if ((vm.getType().equals(VirtualMachine.Type.User)) && (rootVolChainInfo != null) && (!rootVolChainInfo.isEmpty())) { + String rootDiskController = null; + try { + VirtualMachineVolumeChainInfo infoInChain = _gson.fromJson(rootVolChainInfo, VirtualMachineVolumeChainInfo.class); + if (infoInChain != null) { + rootDiskController = infoInChain.getControllerFromDeviceBusName(); + } + UserVmVO userVmVo = _userVmDao.findById(vm.getId()); + if ((rootDiskController != null) && (!rootDiskController.isEmpty())) { + _userVmDao.loadDetails(userVmVo); + _userVmMgr.persistDeviceBusInfo(userVmVo, rootDiskController); + } + } catch (JsonParseException ignored) { + } + } } } } @@ -2349,6 +2379,15 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _volsDao.update(volumeToAttach.getId(), volumeToAttach); } + + if (host.getHypervisorType() == Hypervisor.HypervisorType.VMware) { + Map diskDetails = answer.getDiskDetails(); + for (Map.Entry detail : diskDetails.entrySet()) { + VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeToAttach.getId(), detail.getKey(), detail.getValue(), true); + _volDetailDao.persist(volumeDetailVo); + } + } + } else { _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), deviceId); } diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 324547f47ea..204619a669e 100755 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -111,4 +111,6 @@ public interface UserVmManager extends UserVmService { public void removeCustomOfferingDetails(long vmId); void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType); + + void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString); } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 35aafb8164a..e3ca9e69980 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -5028,6 +5028,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + public void persistDeviceBusInfo(UserVmVO vm, String rootDiskController) { + String existingVmRootDiskController = vm.getDetail(VmDetailConstants.ROOK_DISK_CONTROLLER); + if (((existingVmRootDiskController == null) || (existingVmRootDiskController.isEmpty())) && (rootDiskController != null) && (!rootDiskController.isEmpty())) { + vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, rootDiskController); + _vmDao.saveDetails(vm); + s_logger.debug("Persisted device bus information rootDiskController=" + rootDiskController + " for vm: " + vm.getDisplayName()); + } + } + @Override public String getConfigComponentName() { return UserVmManager.class.getSimpleName(); diff --git a/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java b/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java new file mode 100644 index 00000000000..ac1648b3b94 --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java @@ -0,0 +1,48 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.utils.volume; + +public class VirtualMachineVolumeChainInfo { + String diskDeviceBusName; + String[] diskChain; + + public String getDiskDeviceBusName() { + return this.diskDeviceBusName; + } + + public void setDiskDeviceBusName(String diskDeviceBusName) { + this.diskDeviceBusName = diskDeviceBusName; + } + + public String[] getDiskChain() { + return this.diskChain; + } + + public void setDiskChain(String[] diskChain) { + this.diskChain = diskChain; + } + + public String getControllerFromDeviceBusName() { + if ((this.diskDeviceBusName == null) || (this.diskDeviceBusName.isEmpty()) || (!this.diskDeviceBusName.contains(":"))) { + return null; + } + return this.diskDeviceBusName.substring(0, this.diskDeviceBusName.indexOf(":") - 1); + } +}