Merge remote-tracking branch 'apache/4.20'

This commit is contained in:
Wei Zhou 2025-05-19 21:25:37 +02:00
commit 842b2f8c24
No known key found for this signature in database
GPG Key ID: 1503DFE7C8226103
40 changed files with 1173 additions and 415 deletions

View File

@ -79,6 +79,14 @@ public class UnmanagedInstanceResponse extends BaseResponse {
@Param(description = "the operating system of the virtual machine")
private String operatingSystem;
@SerializedName(ApiConstants.BOOT_MODE)
@Param(description = "indicates the boot mode")
private String bootMode;
@SerializedName(ApiConstants.BOOT_TYPE)
@Param(description = "indicates the boot type")
private String bootType;
@SerializedName(ApiConstants.DISK)
@Param(description = "the list of disks associated with the virtual machine", responseObject = UnmanagedInstanceDiskResponse.class)
private Set<UnmanagedInstanceDiskResponse> disks;
@ -211,4 +219,20 @@ public class UnmanagedInstanceResponse extends BaseResponse {
public void addNic(NicResponse nic) {
this.nics.add(nic);
}
public String getBootMode() {
return bootMode;
}
public void setBootMode(String bootMode) {
this.bootMode = bootMode;
}
public String getBootType() {
return bootType;
}
public void setBootType(String bootType) {
this.bootType = bootType;
}
}

View File

@ -61,6 +61,9 @@ public class UnmanagedInstanceTO {
private String vncPassword;
private String bootType;
private String bootMode;
public String getName() {
return name;
}
@ -196,6 +199,22 @@ public class UnmanagedInstanceTO {
this, "name", "internalCSName", "hostName", "clusterName"));
}
public String getBootType() {
return bootType;
}
public void setBootType(String bootType) {
this.bootType = bootType;
}
public String getBootMode() {
return bootMode;
}
public void setBootMode(String bootMode) {
this.bootMode = bootMode;
}
public static class Disk {
private String diskId;

View File

@ -210,7 +210,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
scanDirectAgentToLoad();
}
private void scanDirectAgentToLoad() {
protected void scanDirectAgentToLoad() {
logger.trace("Begin scanning directly connected hosts");
// for agents that are self-managed, threshold to be considered as disconnected after pingtimeout
@ -231,11 +231,21 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
logger.info("{} is detected down, but we have a forward attache running, disconnect this one before launching the host", host);
removeAgent(agentattache, Status.Disconnected);
} else {
continue;
}
}
logger.debug("Loading directly connected {}", host);
logger.debug("Host {} status is {} but has an AgentAttache which is not forForward, try to load directly", host, host.getStatus());
Status hostStatus = investigate(agentattache);
if (Status.Up == hostStatus) {
/* Got ping response from host, bring it back */
logger.info("After investigation, Agent for host {} is determined to be up and running", host);
agentStatusTransitTo(host, Event.Ping, _nodeId);
} else {
logger.debug("After investigation, AgentAttache is not null but host status is {}, try to load directly {}", hostStatus, host);
loadDirectlyConnectedHost(host, false);
}
}
} else {
logger.debug("AgentAttache is null, loading directly connected {}", host);
loadDirectlyConnectedHost(host, false);
}
} catch (final Throwable e) {
logger.warn(" can not load directly connected {} due to ", host, e);
}
@ -381,7 +391,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
return;
}
if (!result) {
throw new CloudRuntimeException("Failed to propagate agent change request event:" + Event.ShutdownRequested + " to host:" + hostId);
throw new CloudRuntimeException(String.format("Failed to propagate agent change request event: %s to host: %s", Event.ShutdownRequested, hostId));
}
}

View File

@ -61,7 +61,11 @@ import org.apache.cloudstack.ca.CAManager;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.framework.ca.Certificate;
@ -413,6 +417,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
ResourceCleanupService resourceCleanupService;
@Inject
VmWorkJobDao vmWorkJobDao;
@Inject
DataStoreProviderManager dataStoreProviderManager;
private SingleCache<List<Long>> vmIdsInProgressCache;
@ -1238,6 +1244,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
planChangedByVolume = true;
}
}
DataStoreProvider storeProvider = dataStoreProviderManager.getDataStoreProvider(pool.getStorageProviderName());
if (storeProvider != null) {
DataStoreDriver storeDriver = storeProvider.getDataStoreDriver();
if (storeDriver instanceof PrimaryDataStoreDriver) {
((PrimaryDataStoreDriver)storeDriver).detachVolumeFromAllStorageNodes(vol);
}
}
}
}

View File

@ -0,0 +1,150 @@
// 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 com.cloud.agent.manager;
import com.cloud.configuration.ManagementServiceConfiguration;
import com.cloud.ha.HighAvailabilityManagerImpl;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.resource.ResourceManagerImpl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ClusteredAgentManagerImplTest {
private HostDao _hostDao;
@Mock
ManagementServiceConfiguration _mgmtServiceConf;
@Before
public void setUp() throws Exception {
_hostDao = mock(HostDao.class);
}
@Test
public void scanDirectAgentToLoadNoHostsTest() {
ClusteredAgentManagerImpl clusteredAgentManagerImpl = mock(ClusteredAgentManagerImpl.class);
clusteredAgentManagerImpl._hostDao = _hostDao;
clusteredAgentManagerImpl.scanDirectAgentToLoad();
verify(clusteredAgentManagerImpl, never()).findAttache(anyLong());
verify(clusteredAgentManagerImpl, never()).loadDirectlyConnectedHost(any(), anyBoolean());
}
@Test
public void scanDirectAgentToLoadHostWithoutAttacheTest() {
// Arrange
ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class);
HostVO hostVO = mock(HostVO.class);
clusteredAgentManagerImpl._hostDao = _hostDao;
clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf;
clusteredAgentManagerImpl._resourceMgr = mock(ResourceManagerImpl.class);
when(_mgmtServiceConf.getTimeout()).thenReturn(16000L);
when(hostVO.getId()).thenReturn(1L);
List hosts = new ArrayList<>();
hosts.add(hostVO);
when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts);
AgentAttache agentAttache = mock(AgentAttache.class);
doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false);
clusteredAgentManagerImpl.scanDirectAgentToLoad();
verify(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false);
}
@Test
public void scanDirectAgentToLoadHostWithForwardAttacheTest() {
ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class);
HostVO hostVO = mock(HostVO.class);
clusteredAgentManagerImpl._hostDao = _hostDao;
clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf;
when(_mgmtServiceConf.getTimeout()).thenReturn(16000L);
when(hostVO.getId()).thenReturn(1L);
List hosts = new ArrayList<>();
hosts.add(hostVO);
when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts);
AgentAttache agentAttache = mock(AgentAttache.class);
when(agentAttache.forForward()).thenReturn(Boolean.TRUE);
when(clusteredAgentManagerImpl.findAttache(1L)).thenReturn(agentAttache);
clusteredAgentManagerImpl.scanDirectAgentToLoad();
verify(clusteredAgentManagerImpl).removeAgent(agentAttache, Status.Disconnected);
}
@Test
public void scanDirectAgentToLoadHostWithNonForwardAttacheTest() {
// Arrange
ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(new ClusteredAgentManagerImpl());
HostVO hostVO = mock(HostVO.class);
clusteredAgentManagerImpl._hostDao = _hostDao;
clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf;
clusteredAgentManagerImpl._haMgr = mock(HighAvailabilityManagerImpl.class);
when(_mgmtServiceConf.getTimeout()).thenReturn(16000L);
when(hostVO.getId()).thenReturn(0L);
List hosts = new ArrayList<>();
hosts.add(hostVO);
when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts);
AgentAttache agentAttache = mock(AgentAttache.class);
when(agentAttache.forForward()).thenReturn(Boolean.FALSE);
when(clusteredAgentManagerImpl.findAttache(0L)).thenReturn(agentAttache);
doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).agentStatusTransitTo(hostVO, Status.Event.Ping, clusteredAgentManagerImpl._nodeId);
doReturn(Status.Up).when(clusteredAgentManagerImpl).investigate(agentAttache);
clusteredAgentManagerImpl.scanDirectAgentToLoad();
verify(clusteredAgentManagerImpl).investigate(agentAttache);
verify(clusteredAgentManagerImpl).agentStatusTransitTo(hostVO, Status.Event.Ping, clusteredAgentManagerImpl._nodeId);
}
@Test
public void scanDirectAgentToLoadHostWithNonForwardAttacheAndDisconnectedTest() {
ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class);
HostVO hostVO = mock(HostVO.class);
clusteredAgentManagerImpl._hostDao = _hostDao;
clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf;
clusteredAgentManagerImpl._haMgr = mock(HighAvailabilityManagerImpl.class);
clusteredAgentManagerImpl._resourceMgr = mock(ResourceManagerImpl.class);
when(_mgmtServiceConf.getTimeout()).thenReturn(16000L);
when(hostVO.getId()).thenReturn(0L);
List hosts = new ArrayList<>();
hosts.add(hostVO);
when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts);
AgentAttache agentAttache = mock(AgentAttache.class);
when(agentAttache.forForward()).thenReturn(Boolean.FALSE);
when(clusteredAgentManagerImpl.findAttache(0L)).thenReturn(agentAttache);
doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false);
clusteredAgentManagerImpl.scanDirectAgentToLoad();
verify(clusteredAgentManagerImpl).investigate(agentAttache);
verify(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false);
}
}

View File

@ -80,7 +80,7 @@ public class DefaultEndPointSelector implements EndPointSelector {
private final String findOneHostOnPrimaryStorage = "select t.id from "
+ "(select h.id, cd.value, hd.value as " + VOL_ENCRYPT_COLUMN_NAME + " "
+ "from host h join storage_pool_host_ref s on h.id = s.host_id "
+ "join cluster c on c.id=h.cluster_id "
+ "join cluster c on c.id=h.cluster_id and c.allocation_state = 'Enabled'"
+ "left join cluster_details cd on c.id=cd.cluster_id and cd.name='" + CapacityManager.StorageOperationsExcludeCluster.key() + "' "
+ "left join host_details hd on h.id=hd.host_id and hd.name='" + HOST_VOLUME_ENCRYPTION + "' "
+ "where h.status = 'Up' and h.type = 'Routing' and h.resource_state = 'Enabled' and s.pool_id = ? ";

View File

@ -328,6 +328,11 @@ public class VolumeServiceImpl implements VolumeService {
} else {
vo.processEvent(Event.OperationFailed);
errMsg = result.getResult();
VolumeVO volume = volDao.findById(vo.getId());
if (volume != null && volume.getState() == State.Allocated && volume.getPodId() != null) {
volume.setPoolId(null);
volDao.update(volume.getId(), volume);
}
}
VolumeApiResult volResult = new VolumeApiResult((VolumeObject)vo);
if (errMsg != null) {
@ -1255,6 +1260,10 @@ public class VolumeServiceImpl implements VolumeService {
}
if (volume.getState() == State.Allocated) { // Possible states here: Allocated, Ready & Creating
if (volume.getPodId() != null) {
volume.setPoolId(null);
volDao.update(volume.getId(), volume);
}
return;
}
@ -2494,7 +2503,7 @@ public class VolumeServiceImpl implements VolumeService {
try {
volume.processEvent(Event.ResizeRequested);
} catch (Exception e) {
logger.debug("Failed to change state to resize", e);
logger.debug("Failed to change volume state to resize", e);
result.setResult(e.toString());
future.complete(result);
return future;
@ -2506,10 +2515,8 @@ public class VolumeServiceImpl implements VolumeService {
try {
volume.getDataStore().getDriver().resize(volume, caller);
} catch (Exception e) {
logger.debug("Failed to change state to resize", e);
logger.debug("Failed to resize volume", e);
result.setResult(e.toString());
future.complete(result);
}
@ -2553,7 +2560,7 @@ public class VolumeServiceImpl implements VolumeService {
try {
volume.processEvent(Event.OperationFailed);
} catch (Exception e) {
logger.debug("Failed to change state", e);
logger.debug("Failed to change volume state (after resize failure)", e);
}
VolumeApiResult res = new VolumeApiResult(volume);
res.setResult(result.getResult());
@ -2564,13 +2571,8 @@ public class VolumeServiceImpl implements VolumeService {
try {
volume.processEvent(Event.OperationSuccessed);
} catch (Exception e) {
logger.debug("Failed to change state", e);
VolumeApiResult res = new VolumeApiResult(volume);
res.setResult(result.getResult());
future.complete(res);
return null;
logger.debug("Failed to change volume state (after resize success)", e);
}
VolumeApiResult res = new VolumeApiResult(volume);
future.complete(res);

View File

@ -25,6 +25,7 @@ import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
@ -278,6 +279,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
restoredVolume.setPoolId(dataStore.getPoolId());
restoredVolume.setPath(restoredVolume.getUuid());
restoredVolume.setState(Volume.State.Copying);
restoredVolume.setFormat(Storage.ImageFormat.QCOW2);
restoredVolume.setSize(backedUpVolumeSize);
restoredVolume.setDiskOfferingId(volume.getDiskOfferingId());

View File

@ -45,7 +45,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
private static final String MOUNT_COMMAND = "sudo mount -t %s %s %s";
private static final String UMOUNT_COMMAND = "sudo umount %s";
private static final String FILE_PATH_PLACEHOLDER = "%s/%s";
private static final String ATTACH_DISK_COMMAND = " virsh attach-disk %s %s %s --cache none";
private static final String ATTACH_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --subdriver qcow2 --cache none";
private static final String CURRRENT_DEVICE = "virsh domblklist --domain %s | tail -n 3 | head -n 1 | awk '{print $1}'";
private static final String RSYNC_COMMAND = "rsync -az %s %s";

View File

@ -43,6 +43,7 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.util.VmwareClient;
import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd;
import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd;
@ -171,8 +172,11 @@ import com.cloud.vm.dao.VMInstanceDao;
import com.vmware.pbm.PbmProfile;
import com.vmware.vim25.AboutInfo;
import com.vmware.vim25.ManagedObjectReference;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener, VmwareDatacenterService, Configurable {
protected static Logger static_logger = LogManager.getLogger(VmwareManagerImpl.class);
private static final long SECONDS_PER_MINUTE = 60;
private static final int DEFAULT_PORTS_PER_DV_PORT_GROUP_VSPHERE4_x = 256;
@ -1585,14 +1589,26 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
return compatiblePools;
}
@Override
public List<UnmanagedInstanceTO> listVMsInDatacenter(ListVmwareDcVmsCmd cmd) {
private static class VcenterData {
public final String vcenter;
public final String datacenterName;
public final String username;
public final String password;
public VcenterData(String vcenter, String datacenterName, String username, String password) {
this.vcenter = vcenter;
this.datacenterName = datacenterName;
this.username = username;
this.password = password;
}
}
private VcenterData getVcenterData(ListVmwareDcVmsCmd cmd) {
String vcenter = cmd.getVcenter();
String datacenterName = cmd.getDatacenterName();
String username = cmd.getUsername();
String password = cmd.getPassword();
Long existingVcenterId = cmd.getExistingVcenterId();
String keyword = cmd.getKeyword();
if ((existingVcenterId == null && StringUtils.isBlank(vcenter)) ||
(existingVcenterId != null && StringUtils.isNotBlank(vcenter))) {
@ -1613,34 +1629,69 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
username = vmwareDc.getUser();
password = vmwareDc.getPassword();
}
VcenterData vmwaredc = new VcenterData(vcenter, datacenterName, username, password);
return vmwaredc;
}
private static VmwareContext getVmwareContext(String vcenter, String username, String password) throws Exception {
static_logger.debug(String.format("Connecting to the VMware vCenter %s", vcenter));
String serviceUrl = String.format("https://%s/sdk/vimService", vcenter);
VmwareClient vimClient = new VmwareClient(vcenter);
vimClient.connect(serviceUrl, username, password);
return new VmwareContext(vimClient, vcenter);
}
@Override
public List<UnmanagedInstanceTO> listVMsInDatacenter(ListVmwareDcVmsCmd cmd) {
VcenterData vmwareDC = getVcenterData(cmd);
String vcenter = vmwareDC.vcenter;
String username = vmwareDC.username;
String password = vmwareDC.password;
String datacenterName = vmwareDC.datacenterName;
String keyword = cmd.getKeyword();
String esxiHostName = cmd.getHostName();
String virtualMachineName = cmd.getInstanceName();
try {
logger.debug(String.format("Connecting to the VMware datacenter %s at vCenter %s to retrieve VMs",
datacenterName, vcenter));
String serviceUrl = String.format("https://%s/sdk/vimService", vcenter);
VmwareClient vimClient = new VmwareClient(vcenter);
vimClient.connect(serviceUrl, username, password);
VmwareContext context = new VmwareContext(vimClient, vcenter);
VmwareContext context = getVmwareContext(vcenter, username, password);
DatacenterMO dcMo = getDatacenterMO(context, vcenter, datacenterName);
DatacenterMO dcMo = new DatacenterMO(context, datacenterName);
ManagedObjectReference dcMor = dcMo.getMor();
if (dcMor == null) {
String msg = String.format("Unable to find VMware datacenter %s in vCenter %s",
datacenterName, vcenter);
logger.error(msg);
throw new InvalidParameterValueException(msg);
List<UnmanagedInstanceTO> instances;
if (StringUtils.isNotBlank(esxiHostName) && StringUtils.isNotBlank(virtualMachineName)) {
ManagedObjectReference hostMor = dcMo.findHost(esxiHostName);
if (hostMor == null) {
String errorMsg = String.format("Cannot find a host with name %s on vcenter %s", esxiHostName, vcenter);
logger.error(errorMsg);
throw new CloudRuntimeException(errorMsg);
}
List<UnmanagedInstanceTO> instances = dcMo.getAllVmsOnDatacenter();
return StringUtils.isBlank(keyword) ? instances :
instances.stream().filter(x -> x.getName().toLowerCase().contains(keyword.toLowerCase())).collect(Collectors.toList());
HostMO hostMO = new HostMO(context, hostMor);
VirtualMachineMO vmMo = hostMO.findVmOnHyperHost(virtualMachineName);
instances = Collections.singletonList(VmwareHelper.getUnmanagedInstance(hostMO, vmMo));
} else {
instances = dcMo.getAllVmsOnDatacenter(keyword);
}
return instances;
} catch (Exception e) {
String errorMsg = String.format("Error retrieving stopped VMs from the VMware VC %s datacenter %s: %s",
String errorMsg = String.format("Error retrieving VMs from the VMware VC %s datacenter %s: %s",
vcenter, datacenterName, e.getMessage());
logger.error(errorMsg, e);
throw new CloudRuntimeException(errorMsg);
}
}
private static DatacenterMO getDatacenterMO(VmwareContext context, String vcenter, String datacenterName) throws Exception {
DatacenterMO dcMo = new DatacenterMO(context, datacenterName);
ManagedObjectReference dcMor = dcMo.getMor();
if (dcMor == null) {
String msg = String.format("Unable to find VMware datacenter %s in vCenter %s", datacenterName, vcenter);
static_logger.error(msg);
throw new InvalidParameterValueException(msg);
}
return dcMo;
}
@Override
public boolean hasNexusVSM(Long clusterId) {
ClusterVSMMapVO vsmMapVo = null;
@ -1693,7 +1744,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
}
/**
* This task is to cleanup templates from primary storage that are otherwise not cleaned by the {@link com.cloud.storage.StorageManagerImpl.StorageGarbageCollector}.
* This task is to cleanup templates from primary storage that are otherwise not cleaned by the {code}StorageGarbageCollector{code} from {@link com.cloud.storage.StorageManagerImpl}.
* it is called at regular intervals when storage.template.cleanup.enabled == true
* It collect all templates that
* - are deleted from cloudstack

View File

@ -2042,7 +2042,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
VirtualMachineDefinedProfileSpec diskProfileSpec = null;
VirtualMachineDefinedProfileSpec vmProfileSpec = null;
DeployAsIsInfoTO deployAsIsInfo = vmSpec.getDeployAsIsInfo();
boolean deployAsIs = deployAsIsInfo != null;
@ -2086,7 +2085,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
VirtualMachineDiskInfoBuilder diskInfoBuilder = null;
VirtualDevice[] nicDevices = null;
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName);
DiskControllerType systemVmScsiControllerType = DiskControllerType.lsilogic;
int firstScsiControllerBusNum = 0;
@ -2103,7 +2101,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
diskDatastores = vmMo.getAllDiskDatastores();
diskInfoBuilder = vmMo.getDiskInfoBuilder();
hasSnapshot = vmMo.hasSnapshot();
nicDevices = vmMo.getNicDevices();
tearDownVmDevices(vmMo, hasSnapshot, deployAsIs);
ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, systemVmScsiControllerType,
@ -2119,7 +2116,9 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
takeVmFromOtherHyperHost(hyperHost, vmInternalCSName);
vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName);
if (vmMo != null) {
if (getVmPowerState(vmMo) != PowerState.PowerOff)
vmMo.safePowerOff(_shutdownWaitMs);
@ -2130,6 +2129,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
tearDownVmDevices(vmMo, hasSnapshot, deployAsIs);
ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, systemVmScsiControllerType,
numScsiControllerForSystemVm, firstScsiControllerBusNum, deployAsIs);
}
} else {
// If a VM with the same name is found in a different cluster in the DC, unregister the old VM and configure a new VM (cold-migration).
VirtualMachineMO existingVmInDc = dcMo.findVm(vmInternalCSName);
@ -2146,7 +2146,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName);
if (vmMo == null) {
logger.info("Cloned deploy-as-is VM " + vmInternalCSName + " is not in this host, relocating it");
vmMo = takeVmFromOtherHyperHost(hyperHost, vmInternalCSName);
takeVmFromOtherHyperHost(hyperHost, vmInternalCSName);
}
} else {
DiskTO rootDisk = null;
@ -2593,7 +2593,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
Map<String, Map<String, String>> iqnToData = new HashMap<>();
postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, ideControllerKey, scsiControllerKey, iqnToData, hyperHost, context);
postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, iqnToData, hyperHost, context);
//
// Power-on VM
@ -2731,14 +2731,24 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
private boolean powerOnVM(final VirtualMachineMO vmMo, final String vmInternalCSName, final String vmNameOnVcenter) throws Exception {
int retry = 20;
while (retry-- > 0) {
final int retry = 20;
int retryAttempt = 0;
while (++retryAttempt <= retry) {
try {
logger.debug(String.format("VM %s, powerOn attempt #%d", vmInternalCSName, retryAttempt));
return vmMo.powerOn();
} catch (Exception e) {
logger.info(String.format("Got exception while power on VM %s with hostname %s", vmInternalCSName, vmNameOnVcenter), e);
if (e.getMessage() != null && e.getMessage().contains("File system specific implementation of Ioctl[file] failed")) {
if (e.getMessage() != null &&
(e.getMessage().contains("File system specific implementation of Ioctl[file] failed") ||
e.getMessage().contains("Unable to access file") ||
e.getMessage().contains("it is locked"))) {
logger.debug(String.format("Failed to power on VM %s with hostname %s. Retrying", vmInternalCSName, vmNameOnVcenter));
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
logger.debug(String.format("Waiting to power on VM %s been interrupted: ", vmInternalCSName));
}
} else {
throw e;
}
@ -3630,7 +3640,10 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
private VirtualMachineDiskInfo getMatchingExistingDisk(VirtualMachineDiskInfoBuilder diskInfoBuilder, DiskTO vol, VmwareHypervisorHost hyperHost, VmwareContext context)
throws Exception {
if (diskInfoBuilder != null) {
if (diskInfoBuilder == null) {
return null;
}
VolumeObjectTO volume = (VolumeObjectTO) vol.getData();
String chainInfo = volume.getChainInfo();
Map<String, String> details = vol.getDetails();
@ -3639,9 +3652,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
String datastoreUUID = volume.getDataStore().getUuid();
return getMatchingExistingDiskWithVolumeDetails(diskInfoBuilder, volume.getPath(), chainInfo, isManaged, iScsiName, datastoreUUID, hyperHost, context);
} else {
return null;
}
}
private String getDiskController(VirtualMachineMO vmMo, VirtualMachineDiskInfo matchingExistingDisk, DiskTO vol, Pair<String, String> controllerInfo, boolean deployAsIs) throws Exception {
@ -3666,34 +3676,36 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
return VmwareHelper.getControllerBasedOnDiskType(controllerInfo, vol);
}
private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks, int ideControllerKey,
int scsiControllerKey, Map<String, Map<String, String>> iqnToData, VmwareHypervisorHost hyperHost, VmwareContext context) throws Exception {
private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks,
Map<String, Map<String, String>> iqnToData, VmwareHypervisorHost hyperHost, VmwareContext context) throws Exception {
VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
for (DiskTO vol : sortedDisks) {
if (vol.getType() == Volume.Type.ISO)
continue;
VolumeObjectTO volumeTO = (VolumeObjectTO) vol.getData();
VirtualMachineDiskInfo diskInfo = getMatchingExistingDisk(diskInfoBuilder, vol, hyperHost, context);
assert (diskInfo != null);
if (diskInfo == null) {
continue;
}
String[] diskChain = diskInfo.getDiskChain();
assert (diskChain.length > 0);
Map<String, String> details = vol.getDetails();
boolean managed = false;
if (details != null) {
managed = Boolean.parseBoolean(details.get(DiskTO.MANAGED));
if (diskChain.length <= 0) {
continue;
}
DatastoreFile file = new DatastoreFile(diskChain[0]);
boolean managed = false;
Map<String, String> details = vol.getDetails();
if (details != null) {
managed = Boolean.parseBoolean(details.get(DiskTO.MANAGED));
}
VolumeObjectTO volumeTO = (VolumeObjectTO) vol.getData();
if (managed) {
DatastoreFile originalFile = new DatastoreFile(volumeTO.getPath());
if (!file.getFileBaseName().equalsIgnoreCase(originalFile.getFileBaseName())) {
if (logger.isInfoEnabled())
logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " " + volumeTO.getPath() + " -> " + diskChain[0]);
@ -3706,7 +3718,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
VolumeObjectTO volInSpec = getVolumeInSpec(vmSpec, volumeTO);
if (volInSpec != null) {
if (managed) {
Map<String, String> data = new HashMap<>();
@ -3871,7 +3882,8 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
if (diskInfo != null) {
logger.info("Found existing disk info from volume path: " + volume.getPath());
return dsMo;
} else {
}
String chainInfo = volume.getChainInfo();
if (chainInfo != null) {
VirtualMachineDiskInfo infoInChain = _gson.fromJson(chainInfo, VirtualMachineDiskInfo.class);
@ -3890,7 +3902,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
}
}
}
return null;
}
@ -4747,7 +4758,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
Map<Integer, Long> volumeDeviceKey = new HashMap<>();
if (cmd instanceof MigrateVolumeCommand) { // Else device keys will be found in relocateVirtualMachine
MigrateVolumeCommand mcmd = (MigrateVolumeCommand) cmd;
addVolumeDiskmapping(vmMo, volumeDeviceKey, mcmd.getVolumePath(), mcmd.getVolumeId());
addVolumeDiskMapping(vmMo, volumeDeviceKey, mcmd.getVolumePath(), mcmd.getVolumeId());
if (logger.isTraceEnabled()) {
for (Integer diskId: volumeDeviceKey.keySet()) {
logger.trace(String.format("Disk to migrate has disk id %d and volumeId %d", diskId, volumeDeviceKey.get(diskId)));
@ -4765,9 +4776,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
Answer createAnswerForCmd(VirtualMachineMO vmMo, List<VolumeObjectTO> volumeObjectToList, Command cmd, Map<Integer, Long> volumeDeviceKey) throws Exception {
List<VolumeObjectTO> volumeToList;
VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
VirtualDisk[] disks = vmMo.getAllDiskDevice();
Answer answer;
if (logger.isTraceEnabled()) {
logger.trace(String.format("creating answer for %s", cmd.getClass().getSimpleName()));
}
@ -4784,7 +4793,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
return new Answer(cmd, false, null);
}
private void addVolumeDiskmapping(VirtualMachineMO vmMo, Map<Integer, Long> volumeDeviceKey, String volumePath, long volumeId) throws Exception {
private void addVolumeDiskMapping(VirtualMachineMO vmMo, Map<Integer, Long> volumeDeviceKey, String volumePath, long volumeId) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug(String.format("locating disk for volume (%d) using path %s", volumeId, volumePath));
}
@ -5886,6 +5895,11 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
logger.debug(msg);
return new Answer(cmd, true, msg);
} catch (Exception e) {
if (e.getMessage().contains("was not found")) {
String msg = String.format("%s - VM [%s] file(s) not found, cleanup not needed .", e.getMessage(), cmd.getVmName());
logger.debug(msg);
return new Answer(cmd, true, msg);
}
return new Answer(cmd, false, createLogMessageException(e, cmd));
}
}

View File

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.vmware.vim25.ManagedObjectReference;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.logging.log4j.Logger;
@ -193,7 +194,7 @@ public class VmwareStorageLayoutHelper implements Configurable {
if (ds.fileExists(vmdkFullCloneModeLegacyPair[i])) {
LOGGER.info("sync " + vmdkFullCloneModeLegacyPair[i] + "->" + vmdkFullCloneModePair[i]);
ds.moveDatastoreFile(vmdkFullCloneModeLegacyPair[i], dcMo.getMor(), ds.getMor(), vmdkFullCloneModePair[i], dcMo.getMor(), true);
moveDatastoreFile(ds, vmdkFullCloneModeLegacyPair[i], dcMo.getMor(), ds.getMor(), vmdkFullCloneModePair[i], dcMo.getMor(), true);
}
}
@ -201,13 +202,13 @@ public class VmwareStorageLayoutHelper implements Configurable {
if (ds.fileExists(vmdkLinkedCloneModeLegacyPair[i])) {
LOGGER.info("sync " + vmdkLinkedCloneModeLegacyPair[i] + "->" + vmdkLinkedCloneModePair[i]);
ds.moveDatastoreFile(vmdkLinkedCloneModeLegacyPair[i], dcMo.getMor(), ds.getMor(), vmdkLinkedCloneModePair[i], dcMo.getMor(), true);
moveDatastoreFile(ds, vmdkLinkedCloneModeLegacyPair[i], dcMo.getMor(), ds.getMor(), vmdkLinkedCloneModePair[i], dcMo.getMor(), true);
}
}
if (ds.fileExists(vmdkLinkedCloneModeLegacyPair[0])) {
LOGGER.info("sync " + vmdkLinkedCloneModeLegacyPair[0] + "->" + vmdkLinkedCloneModePair[0]);
ds.moveDatastoreFile(vmdkLinkedCloneModeLegacyPair[0], dcMo.getMor(), ds.getMor(), vmdkLinkedCloneModePair[0], dcMo.getMor(), true);
moveDatastoreFile(ds, vmdkLinkedCloneModeLegacyPair[0], dcMo.getMor(), ds.getMor(), vmdkLinkedCloneModePair[0], dcMo.getMor(), true);
}
// Note: we will always return a path
@ -242,14 +243,14 @@ public class VmwareStorageLayoutHelper implements Configurable {
String targetPath = getDatastorePathBaseFolderFromVmdkFileName(ds, String.format("%s-%s",vmdkName, linkedCloneExtension));
LOGGER.info("Fixup folder-synchronization. move " + companionFilePath + " -> " + targetPath);
ds.moveDatastoreFile(companionFilePath, dcMo.getMor(), ds.getMor(), targetPath, dcMo.getMor(), true);
moveDatastoreFile(ds, companionFilePath, dcMo.getMor(), ds.getMor(), targetPath, dcMo.getMor(), true);
}
}
// move the identity VMDK file the last
String targetPath = getDatastorePathBaseFolderFromVmdkFileName(ds, vmdkName + ".vmdk");
LOGGER.info("Fixup folder-synchronization. move " + fileDsFullPath + " -> " + targetPath);
ds.moveDatastoreFile(fileDsFullPath, dcMo.getMor(), ds.getMor(), targetPath, dcMo.getMor(), true);
moveDatastoreFile(ds, fileDsFullPath, dcMo.getMor(), ds.getMor(), targetPath, dcMo.getMor(), true);
try {
if (folderName != null) {
@ -287,7 +288,7 @@ public class VmwareStorageLayoutHelper implements Configurable {
DatastoreFile targetFile = new DatastoreFile(file.getDatastoreName(), HypervisorHostHelper.VSPHERE_DATASTORE_BASE_FOLDER, file.getFileName());
if (!targetFile.getPath().equalsIgnoreCase(file.getPath())) {
LOGGER.info("Move " + file.getPath() + " -> " + targetFile.getPath());
dsMo.moveDatastoreFile(file.getPath(), dcMo.getMor(), dsMo.getMor(), targetFile.getPath(), dcMo.getMor(), true);
moveDatastoreFile(dsMo, file.getPath(), dcMo.getMor(), dsMo.getMor(), targetFile.getPath(), dcMo.getMor(), true);
List<String> vSphereFileExtensions = new ArrayList<>(Arrays.asList(VsphereLinkedCloneExtensions.value().trim().split("\\s*,\\s*")));
// add flat file format to the above list
@ -297,7 +298,7 @@ public class VmwareStorageLayoutHelper implements Configurable {
String pairTargetFilePath = targetFile.getCompanionPath(String.format("%s-%s", file.getFileBaseName(), linkedCloneExtension));
if (dsMo.fileExists(pairSrcFilePath)) {
LOGGER.info("Move " + pairSrcFilePath + " -> " + pairTargetFilePath);
dsMo.moveDatastoreFile(pairSrcFilePath, dcMo.getMor(), dsMo.getMor(), pairTargetFilePath, dcMo.getMor(), true);
moveDatastoreFile(dsMo, pairSrcFilePath, dcMo.getMor(), dsMo.getMor(), pairTargetFilePath, dcMo.getMor(), true);
}
}
}
@ -429,6 +430,31 @@ public class VmwareStorageLayoutHelper implements Configurable {
return dsMo.searchFileInSubFolders(volumePath + ".vmdk", false, null);
}
public static boolean moveDatastoreFile(final DatastoreMO dsMo, String srcFilePath, ManagedObjectReference morSrcDc, ManagedObjectReference morDestDs,
String destFilePath, ManagedObjectReference morDestDc, boolean forceOverwrite) throws Exception {
final int retry = 20;
int retryAttempt = 0;
while (++retryAttempt <= retry) {
try {
LOGGER.debug(String.format("Move datastore file %s, attempt #%d", srcFilePath, retryAttempt));
return dsMo.moveDatastoreFile(srcFilePath, morSrcDc, morDestDs, destFilePath, morDestDc, forceOverwrite);
} catch (Exception e) {
LOGGER.info(String.format("Got exception while moving datastore file %s ", srcFilePath), e);
if (e.getMessage() != null && e.getMessage().contains("Unable to access file")) {
LOGGER.debug(String.format("Failed to move datastore file %s. Retrying", srcFilePath));
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
LOGGER.debug(String.format("Waiting to move datastore file %s been interrupted: ", srcFilePath));
}
} else {
throw e;
}
}
}
return false;
}
@Override
public String getConfigComponentName() {
return VmwareStorageLayoutHelper.class.getSimpleName();

View File

@ -682,9 +682,9 @@ public class VmwareStorageProcessor implements StorageProcessor {
String[] legacyCloudStackLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairManagedDatastorePath(dsMo, null,
managedStoragePoolRootVolumeName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, false);
dsMo.moveDatastoreFile(vmwareLayoutFilePair[0], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[0], dcMo.getMor(), true);
VmwareStorageLayoutHelper.moveDatastoreFile(dsMo, vmwareLayoutFilePair[0], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[0], dcMo.getMor(), true);
for (int i=1; i<vmwareLayoutFilePair.length; i++) {
dsMo.moveDatastoreFile(vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true);
VmwareStorageLayoutHelper.moveDatastoreFile(dsMo, vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true);
}
String folderToDelete = dsMo.getDatastorePath(managedStoragePoolRootVolumeName, true);
@ -814,7 +814,7 @@ public class VmwareStorageProcessor implements StorageProcessor {
existingVm.detachAllDisksAndDestroy();
}
logger.info("ROOT Volume from deploy-as-is template, cloning template");
cloneVMFromTemplate(hyperHost, template.getPath(), vmName, primaryStore.getUuid());
cloneVMFromTemplate(hyperHost, template, volume, vmName, primaryStore.getUuid());
} else {
logger.info("ROOT Volume from deploy-as-is template, volume already created at this point");
}
@ -945,7 +945,7 @@ public class VmwareStorageProcessor implements StorageProcessor {
String[] legacyCloudStackLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkFileBaseName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, !_fullCloneFlag);
for (int i = 0; i < vmwareLayoutFilePair.length; i++) {
dsMo.moveDatastoreFile(vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true);
VmwareStorageLayoutHelper.moveDatastoreFile(dsMo, vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true);
}
logger.info("detach disks from volume-wrapper VM and destroy {}", vmdkName);
@ -3811,8 +3811,9 @@ public class VmwareStorageProcessor implements StorageProcessor {
/**
* Return the cloned VM from the template
*/
public VirtualMachineMO cloneVMFromTemplate(VmwareHypervisorHost hyperHost, String templateName, String cloneName, String templatePrimaryStoreUuid) {
public VirtualMachineMO cloneVMFromTemplate(VmwareHypervisorHost hyperHost, TemplateObjectTO template, VolumeObjectTO volume, String cloneName, String templatePrimaryStoreUuid) {
try {
String templateName = template.getPath();
VmwareContext context = hyperHost.getContext();
DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter());
VirtualMachineMO templateMo = dcMo.findVm(templateName);
@ -3826,6 +3827,9 @@ public class VmwareStorageProcessor implements StorageProcessor {
throw new CloudRuntimeException("Unable to find datastore in vSphere");
}
logger.info("Cloning VM " + cloneName + " from template " + templateName + " into datastore " + templatePrimaryStoreUuid);
if (template.getSize() != null) {
_fullCloneFlag = volume.getSize() > template.getSize() ? true : _fullCloneFlag;
}
if (!_fullCloneFlag) {
createVMLinkedClone(templateMo, dcMo, cloneName, morDatastore, morPool, null);
} else {

View File

@ -70,6 +70,12 @@ public class ListVmwareDcVmsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for specified username.")
private String password;
@Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, description = "Name of the host on vCenter. Must be set along with the instancename parameter")
private String hostName;
@Parameter(name = ApiConstants.INSTANCE_NAME, type = CommandType.STRING, description = "Name of the VM on vCenter. Must be set along with the hostname parameter")
private String instanceName;
public String getVcenter() {
return vcenter;
}
@ -86,10 +92,18 @@ public class ListVmwareDcVmsCmd extends BaseListCmd {
return datacenterName;
}
public String getHostName() {
return hostName;
}
public Long getExistingVcenterId() {
return existingVcenterId;
}
public String getInstanceName() {
return instanceName;
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
checkParameters();
@ -125,6 +139,11 @@ public class ListVmwareDcVmsCmd extends BaseListCmd {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
"Please set all the information for a vCenter IP/Name, datacenter, username and password");
}
if ((StringUtils.isNotBlank(instanceName) && StringUtils.isBlank(hostName)) ||
(StringUtils.isBlank(instanceName) && StringUtils.isNotBlank(hostName))) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
"Please set the hostname parameter along with the instancename parameter");
}
}
@Override

View File

@ -453,9 +453,18 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
boolean encryptionRequired = anyVolumeRequiresEncryption(vol);
long [] endpointsToRunResize = resizeParameter.hosts;
CreateCmdResult result = new CreateCmdResult(null, null);
// if hosts are provided, they are where the VM last ran. We can use that.
if (endpointsToRunResize == null || endpointsToRunResize.length == 0) {
EndPoint ep = epSelector.select(data, encryptionRequired);
if (ep == null) {
String errMsg = String.format(NO_REMOTE_ENDPOINT_WITH_ENCRYPTION, encryptionRequired);
logger.error(errMsg);
result.setResult(errMsg);
callback.complete(result);
return;
}
endpointsToRunResize = new long[] {ep.getId()};
}
ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(),
@ -463,7 +472,6 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
if (pool.getParent() != 0) {
resizeCmd.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
}
CreateCmdResult result = new CreateCmdResult(null, null);
try {
ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, endpointsToRunResize, resizeCmd);
if (answer != null && answer.getResult()) {
@ -480,7 +488,6 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
logger.debug("return a null answer, mark it as failed for unknown reason");
result.setResult("return a null answer, mark it as failed for unknown reason");
}
} catch (Exception e) {
logger.debug("sending resize command failed", e);
result.setResult(e.toString());

View File

@ -18,7 +18,6 @@ package org.apache.cloudstack.storage.datastore.provider;
import com.cloud.exception.StorageConflictException;
import com.cloud.host.HostVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
public class LinstorHostListener extends DefaultHostListener {
@Override
@ -28,7 +27,6 @@ public class LinstorHostListener extends DefaultHostListener {
host.setParent(host.getName());
hostDao.update(host.getId(), host);
}
StoragePoolVO pool = primaryStoreDao.findById(poolId);
return super.hostConnect(host, pool);
return super.hostConnect(hostId, poolId);
}
}

View File

@ -5291,6 +5291,8 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setMemory(instance.getMemory());
response.setOperatingSystemId(instance.getOperatingSystemId());
response.setOperatingSystem(instance.getOperatingSystem());
response.setBootMode(instance.getBootMode());
response.setBootType(instance.getBootType());
response.setObjectName("unmanagedinstance");
if (instance.getDisks() != null) {

View File

@ -4569,14 +4569,24 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
String endIP = cmd.getEndIp();
final String newVlanGateway = cmd.getGateway();
final String newVlanNetmask = cmd.getNetmask();
Long networkId = cmd.getNetworkID();
Long physicalNetworkId = cmd.getPhysicalNetworkId();
// Verify that network exists
Network network = getNetwork(networkId);
if (network != null) {
zoneId = network.getDataCenterId();
physicalNetworkId = network.getPhysicalNetworkId();
}
String vlanId = cmd.getVlan();
vlanId = verifyAndUpdateVlanId(vlanId, network);
// TODO decide if we should be forgiving or demand a valid and complete URI
if (!(vlanId == null || "".equals(vlanId) || vlanId.startsWith(BroadcastDomainType.Vlan.scheme()))) {
vlanId = BroadcastDomainType.Vlan.toUri(vlanId).toString();
}
final Boolean forVirtualNetwork = cmd.isForVirtualNetwork();
Long networkId = cmd.getNetworkID();
Long physicalNetworkId = cmd.getPhysicalNetworkId();
final String accountName = cmd.getAccountName();
final Long projectId = cmd.getProjectId();
final Long domainId = cmd.getDomainId();
@ -4649,18 +4659,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
// Verify that network exists
Network network = null;
if (networkId != null) {
network = _networkDao.findById(networkId);
if (network == null) {
throw new InvalidParameterValueException("Unable to find network by id " + networkId);
} else {
zoneId = network.getDataCenterId();
physicalNetworkId = network.getPhysicalNetworkId();
}
}
// Verify that zone exists
final DataCenterVO zone = _zoneDao.findById(zoneId);
if (zone == null) {
@ -4799,6 +4797,32 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
ip6Cidr, domain, vlanOwner, network, sameSubnet, cmd.isForNsx());
}
private Network getNetwork(Long networkId) {
if (networkId == null) {
return null;
}
Network network = _networkDao.findById(networkId);
if (network == null) {
throw new InvalidParameterValueException("Unable to find network by id " + networkId);
}
return network;
}
private String verifyAndUpdateVlanId(String vlanId, Network network) {
if (!StringUtils.isBlank(vlanId)) {
return vlanId;
}
if (network == null || network.getTrafficType() != TrafficType.Guest) {
return Vlan.UNTAGGED;
}
boolean connectivityWithoutVlan = isConnectivityWithoutVlan(network);
return getNetworkVlanId(network, connectivityWithoutVlan);
}
private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, final String newVlanNetmaskFinal,
final String vlanId, final Boolean forVirtualNetwork, final Boolean forSystemVms, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6,
final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair<Boolean, Pair<String, String>> sameSubnet, boolean forNsx) {
@ -5007,28 +5031,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
// same as network's vlan
// 2) if vlan is missing, default it to the guest network's vlan
if (network.getTrafficType() == TrafficType.Guest) {
String networkVlanId = null;
boolean connectivityWithoutVlan = false;
if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Connectivity)) {
Map<Capability, String> connectivityCapabilities = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Connectivity);
connectivityWithoutVlan = MapUtils.isNotEmpty(connectivityCapabilities) && connectivityCapabilities.containsKey(Capability.NoVlan);
}
final URI uri = network.getBroadcastUri();
if (connectivityWithoutVlan) {
networkVlanId = network.getBroadcastDomainType().toUri(network.getUuid()).toString();
} else if (uri != null) {
// Do not search for the VLAN tag when the network doesn't support VLAN
if (uri.toString().startsWith("vlan")) {
final String[] vlan = uri.toString().split("vlan:\\/\\/");
networkVlanId = vlan[1];
// For pvlan
if (network.getBroadcastDomainType() != BroadcastDomainType.Vlan) {
networkVlanId = networkVlanId.split("-")[0];
}
}
}
boolean connectivityWithoutVlan = isConnectivityWithoutVlan(network);
String networkVlanId = getNetworkVlanId(network, connectivityWithoutVlan);
if (vlanId != null && !connectivityWithoutVlan) {
// if vlan is specified, throw an error if it's not equal to
// network's vlanId
@ -5160,6 +5164,36 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return vlan;
}
private boolean isConnectivityWithoutVlan(Network network) {
boolean connectivityWithoutVlan = false;
if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Connectivity)) {
Map<Capability, String> connectivityCapabilities = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Connectivity);
connectivityWithoutVlan = MapUtils.isNotEmpty(connectivityCapabilities) && connectivityCapabilities.containsKey(Capability.NoVlan);
}
return connectivityWithoutVlan;
}
private String getNetworkVlanId(Network network, boolean connectivityWithoutVlan) {
String networkVlanId = null;
if (connectivityWithoutVlan) {
return network.getBroadcastDomainType().toUri(network.getUuid()).toString();
}
final URI uri = network.getBroadcastUri();
if (uri != null) {
// Do not search for the VLAN tag when the network doesn't support VLAN
if (uri.toString().startsWith("vlan")) {
final String[] vlan = uri.toString().split("vlan:\\/\\/");
networkVlanId = vlan[1];
// For pvlan
if (network.getBroadcastDomainType() != BroadcastDomainType.Vlan) {
networkVlanId = networkVlanId.split("-")[0];
}
}
}
return networkVlanId;
}
private void checkZoneVlanIpOverlap(DataCenterVO zone, Network network, String newCidr, String vlanId, String vlanGateway, String vlanNetmask, String startIP, String endIP) {
// Throw an exception if this subnet overlaps with subnet on other VLAN,
// if this is ip range extension, gateway, network mask should be same and ip range should not overlap

View File

@ -1030,7 +1030,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
} else if (intervalTypeStr != null && volumeId != null) {
Type type = SnapshotVO.getSnapshotType(intervalTypeStr);
if (type == null) {
throw new InvalidParameterValueException("Unsupported snapstho interval type " + intervalTypeStr);
throw new InvalidParameterValueException("Unsupported snapshot interval type " + intervalTypeStr);
}
sc.setParameters("snapshotTypeEQ", type.ordinal());
} else {

View File

@ -26,8 +26,6 @@ import java.util.TimeZone;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.domain.Domain;
import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd;
import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd;
import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd;
@ -42,6 +40,7 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import com.cloud.configuration.Config;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.InvalidParameterValueException;
@ -58,6 +57,8 @@ import com.cloud.network.rules.PortForwardingRuleVO;
import com.cloud.network.rules.dao.PortForwardingRulesDao;
import com.cloud.network.security.SecurityGroupVO;
import com.cloud.network.security.dao.SecurityGroupDao;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
import com.cloud.storage.SnapshotVO;
@ -72,6 +73,7 @@ import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager;
import com.cloud.utils.component.ManagerBase;
@ -121,6 +123,8 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
private IPAddressDao _ipDao;
@Inject
private HostDao _hostDao;
@Inject
private NetworkOfferingDao _networkOfferingDao;
public UsageServiceImpl() {
}
@ -245,6 +249,7 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
}
Long usageDbId = null;
boolean offeringExistsForNetworkOfferingType = false;
switch (usageType.intValue()) {
case UsageTypes.NETWORK_BYTES_RECEIVED:
@ -318,13 +323,19 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
usageDbId = ip.getId();
}
break;
case UsageTypes.NETWORK_OFFERING:
NetworkOfferingVO networkOffering = _networkOfferingDao.findByUuidIncludingRemoved(usageId);
if (networkOffering != null) {
offeringExistsForNetworkOfferingType = true;
sc.addAnd("offeringId", SearchCriteria.Op.EQ, networkOffering.getId());
}
default:
break;
}
if (usageDbId != null) {
sc.addAnd("usageId", SearchCriteria.Op.EQ, usageDbId);
} else {
} else if (!offeringExistsForNetworkOfferingType) {
// return an empty list if usageId was not found
return new Pair<List<? extends Usage>, Integer>(new ArrayList<Usage>(), new Integer(0));
}

View File

@ -8444,6 +8444,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
getRootVolumeSizeForVmRestore(newVol, template, userVm, diskOffering, details, true);
volumeMgr.saveVolumeDetails(newVol.getDiskOfferingId(), newVol.getId());
newVol = _volsDao.findById(newVol.getId());
// 1. Save usage event and update resource count for user vm volumes
try {
@ -8543,7 +8544,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Long getRootVolumeSizeForVmRestore(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Map<String, String> details, boolean update) {
VolumeVO resizedVolume = (VolumeVO) vol;
Long size = null;
if (template != null && template.getSize() != null) {
UserVmDetailVO vmRootDiskSizeDetail = userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE);

View File

@ -55,6 +55,8 @@ import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.offering.DiskOffering;
@ -196,6 +198,8 @@ public class ConfigurationManagerTest {
@Mock
HostPodDao _podDao;
@Mock
NetworkDao _networkDao;
@Mock
PhysicalNetworkDao _physicalNetworkDao;
@Mock
ImageStoreDao _imageStoreDao;
@ -1331,6 +1335,8 @@ public class ConfigurationManagerTest {
public void testWrongIpv6CreateVlanAndPublicIpRange() {
CreateVlanIpRangeCmd cmd = Mockito.mock(CreateVlanIpRangeCmd.class);
Mockito.when(cmd.getIp6Cidr()).thenReturn("fd17:5:8a43:e2a4:c000::/66");
NetworkVO network = Mockito.mock(NetworkVO.class);
Mockito.when(_networkDao.findById(Mockito.anyLong())).thenReturn(network);
try {
configurationMgr.createVlanAndPublicIpRange(cmd);
} catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException e) {

View File

@ -1089,6 +1089,12 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
try {
_itMgr.expunge(ssvm.getUuid());
ssvm.setPublicIpAddress(null);
ssvm.setPublicMacAddress(null);
ssvm.setPublicNetmask(null);
ssvm.setPrivateMacAddress(null);
ssvm.setPrivateIpAddress(null);
_secStorageVmDao.update(ssvm.getId(), ssvm);
_secStorageVmDao.remove(ssvm.getId());
HostVO host = _hostDao.findByTypeNameAndZoneId(ssvm.getDataCenterId(), ssvm.getHostName(), Host.Type.SecondaryStorageVM);
if (host != null) {
@ -1373,7 +1379,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
@Override
public void finalizeExpunge(VirtualMachine vm) {
SecondaryStorageVmVO ssvm = _secStorageVmDao.findByUuid(vm.getUuid());
ssvm.setPrivateMacAddress(null);
ssvm.setPublicIpAddress(null);
ssvm.setPublicMacAddress(null);
ssvm.setPublicNetmask(null);

View File

@ -148,6 +148,10 @@ class TestRestoreVM(cloudstackTestCase):
self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready state")
self.assertEqual(root_vol.size, 16 * 1024 * 1024 * 1024, "Size of volume and custom disk size should match")
if self.hypervisor.lower() == "vmware":
old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)
self.assertEqual(old_root_vol, None, "Old volume should be deleted")
else:
old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)[0]
self.assertEqual(old_root_vol.state, "Destroy", "Old volume should be in Destroy state")
Volume.delete(old_root_vol, self.apiclient)

View File

@ -988,6 +988,9 @@ class TestSSVMs(cloudstackTestCase):
# Private IP Address of System VMs are allowed to change after reboot - CLOUDSTACK-7745
# Agent in Up state for a while after reboot, wait for the agent to Disconnect and back Up.
time.sleep(60)
# Wait for the agent to be up
self.waitForSystemVMAgent(cpvm_response.name)
@ -1103,6 +1106,9 @@ class TestSSVMs(cloudstackTestCase):
"Check whether CPVM is running or not"
)
# Agent in Up state for a while after reboot, wait for the agent to Disconnect and back Up.
time.sleep(60)
# Wait for the agent to be up
self.waitForSystemVMAgent(cpvm_response.name)

View File

@ -1080,7 +1080,7 @@ test_data = {
"format": "vhd",
"hypervisor": "xenserver",
"ostype": "Other Linux (64-bit)",
"url": "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64-azure.vhd.tar.gz",
"url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64-azure.vhd.tar.gz",
"requireshvm": "True",
"ispublic": "True",
"isextractable": "True"
@ -1091,10 +1091,10 @@ test_data = {
"format": "ova",
"hypervisor": "vmware",
"ostype": "Other Linux (64-bit)",
"url": "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.ova",
"url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.ova",
"requireshvm": "True",
"ispublic": "True",
"deployasis": "True"
"deployasis": "False"
},
},
"test_ovf_templates": [

1
ui/.env.qa Normal file
View File

@ -0,0 +1 @@
CS_URL=https://qa.cloudstack.cloud/simulator/pr/10580

View File

@ -2119,6 +2119,7 @@
"label.sharewith": "Share with",
"label.showing": "Showing",
"label.show.usage.records": "Show usage records",
"label.showing.results.for": "Showing results for \"%x\"",
"label.shrinkok": "Shrink OK",
"label.shutdown": "Shutdown",
"label.shutdown.provider": "Shutdown provider",

View File

@ -17,112 +17,75 @@
<template>
<span class="header-notice-opener">
<a-select
v-if="!isDisabled()"
<infinite-scroll-select
v-if="!isDisabled"
v-model:value="selectedProjectId"
class="project-select"
:loading="loading"
v-model:value="projectSelected"
:filterOption="filterProject"
@change="changeProject"
@focus="fetchData"
showSearch>
<a-select-option
v-for="(project, index) in projects"
:key="index"
:label="project.displaytext || project.name">
<span>
<resource-icon v-if="project.icon && project.icon.base64image" :image="project.icon.base64image" size="1x" style="margin-right: 5px"/>
<project-outlined v-else style="margin-right: 5px" />
{{ project.displaytext || project.name }}
</span>
</a-select-option>
</a-select>
api="listProjects"
:apiParams="projectsApiParams"
resourceType="project"
:defaultOption="defaultOption"
defaultIcon="project-outlined"
:pageSize="100"
@change-option="changeProject" />
</span>
</template>
<script>
import store from '@/store'
import { api } from '@/api'
import _ from 'lodash'
import ResourceIcon from '@/components/view/ResourceIcon'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect'
export default {
name: 'ProjectMenu',
components: {
ResourceIcon
InfiniteScrollSelect
},
data () {
return {
projects: [],
selectedProjectId: null,
loading: false
}
},
created () {
this.fetchData()
this.selectedProjectId = this.$store.getters?.project?.id || this.defaultOption.id
this.$store.dispatch('ToggleTheme', this.selectedProjectId ? 'dark' : 'light')
},
computed: {
projectSelected () {
let projectIndex = 0
if (this.$store.getters?.project?.id) {
projectIndex = this.projects.findIndex(project => project.id === this.$store.getters.project.id)
this.$store.dispatch('ToggleTheme', projectIndex === undefined ? 'light' : 'dark')
isDisabled () {
return !('listProjects' in this.$store.getters.apis)
},
defaultOption () {
return { id: 0, name: this.$t('label.default.view') }
},
projectsApiParams () {
return {
details: 'min',
listall: true
}
return projectIndex
}
},
mounted () {
this.unwatchProject = this.$store.watch(
(state, getters) => getters.project?.id,
(newId) => {
this.selectedProjectId = newId
}
)
},
beforeUnmount () {
if (this.unwatchProject) {
this.unwatchProject()
}
},
methods: {
fetchData () {
if (this.isDisabled()) {
return
}
var page = 1
const projects = []
const getNextPage = () => {
this.loading = true
api('listProjects', { listAll: true, page: page, pageSize: 500, details: 'min', showIcon: true }).then(json => {
if (json?.listprojectsresponse?.project) {
projects.push(...json.listprojectsresponse.project)
}
if (projects.length < json.listprojectsresponse.count) {
page++
getNextPage()
}
}).finally(() => {
this.loading = false
this.$store.commit('RELOAD_ALL_PROJECTS', projects)
})
}
getNextPage()
},
isDisabled () {
return !Object.prototype.hasOwnProperty.call(store.getters.apis, 'listProjects')
},
changeProject (index) {
const project = this.projects[index]
changeProject (project) {
this.$store.dispatch('ProjectView', project.id)
this.$store.dispatch('SetProject', project)
this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
this.$store.dispatch('ToggleTheme', project.id ? 'dark' : 'light')
this.$message.success(`${this.$t('message.switch.to')} "${project.displaytext || project.name}"`)
if (this.$route.name !== 'dashboard') {
this.$router.push({ name: 'dashboard' })
}
},
filterProject (input, option) {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
},
mounted () {
this.$store.watch(
(state, getters) => getters.allProjects,
(newValue, oldValue) => {
if (oldValue !== newValue && newValue !== undefined) {
this.projects = _.orderBy(newValue, ['displaytext'], ['asc'])
this.projects.unshift({ name: this.$t('label.default.view') })
}
}
)
}
}
</script>

View File

@ -0,0 +1,298 @@
// 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.
<!--
InfiniteScrollSelect.vue
A reusable select component that supports:
- Infinite scrolling with paginated API
- Dynamic search filtering. Needs minimum
- Deduplicated option loading
- Auto-fetching of preselected value if not present in the initial result
Usage Example:
<infinite-scroll-select
v-model:value="form.account"
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
optionValueKey="name"
optionLabelKey="name"
@change-option-value="handleAccountNameChange" />
Props:
- api (String, required): API command name (e.g., 'listAccounts')
- apiParams (Object, optional): Additional parameters passed to the API
- resourceType (String, required): The key in the API response containing the resource array (e.g., 'account')
- optionValueKey (String, optional): Property to use as the value for options (e.g., 'name'). Default is 'id'
- optionLabelKey (String, optional): Property to use as the label for options (e.g., 'name'). Default is 'name'
- defaultOption (Object, optional): Preselected object to include initially
- showIcon (Boolean, optional): Whether to show icon for the options. Default is true
- defaultIcon (String, optional): Icon to be shown when there is no resource icon for the option. Default is 'cloud-outlined'
Events:
- @change-option-value (Function): Emits the selected option value(s) when value(s) changes. Do not use @change as it will give warnings and may not work
- @change-option (Function): Emits the selected option object when value changes. Works only when mode is not multiple
Features:
- Debounced remote filtering
- Custom dropdown footer/header (e.g., clear search button)
- Handles preselection and fetches missing option automatically
-->
<template>
<a-select
:filter-option="false"
:loading="loading"
show-search
placeholder="Select"
@search="onSearchTimed"
@popupScroll="onScroll"
@change="onChange"
>
<template #dropdownRender="{ menuNode: menu }">
<v-nodes :vnodes="menu" />
<div v-if="!!searchQuery">
<a-divider style="margin: 4px 0" />
<div class="select-list-footer">
<span>{{ formattedSearchFooterMessage }}</span>
<close-outlined
@mousedown="e => e.preventDefault()"
@click="onSearch()" />
</div>
</div>
</template>
<a-select-option v-for="option in options" :key="option.id" :value="option[optionValueKey]">
<span>
<span v-if="showIcon">
<resource-icon v-if="option.icon && option.icon.base64image" :image="option.icon.base64image" size="1x" style="margin-right: 5px"/>
<render-icon v-else :icon="defaultIcon" style="margin-right: 5px" />
</span>
<span>{{ option[optionLabelKey] }}</span>
</span>
</a-select-option>
</a-select>
</template>
<script>
import { api } from '@/api'
import ResourceIcon from '@/components/view/ResourceIcon'
export default {
name: 'InfiniteScrollSelect',
components: {
ResourceIcon,
VNodes: (_, { attrs }) => {
return attrs.vnodes
}
},
props: {
api: {
type: String,
required: true
},
apiParams: {
type: Object,
required: null
},
resourceType: {
type: String,
required: true
},
optionValueKey: {
type: String,
default: 'id'
},
optionLabelKey: {
type: String,
default: 'name'
},
defaultOption: {
type: Object,
default: null
},
showIcon: {
type: Boolean,
default: true
},
defaultIcon: {
type: String,
default: 'cloud-outlined'
},
pageSize: {
type: Number,
default: null
}
},
data () {
return {
options: [],
page: 1,
totalCount: null,
loading: false,
searchQuery: '',
searchTimer: null,
scrollHandlerAttached: false,
preselectedOptionValue: null,
successiveFetches: 0
}
},
created () {
this.addDefaultOptionIfNeeded(true)
},
mounted () {
this.preselectedOptionValue = this.$attrs.value
this.fetchItems()
},
computed: {
maxSuccessiveFetches () {
return 10
},
computedPageSize () {
return this.pageSize || this.$store.getters.defaultListViewPageSize
},
formattedSearchFooterMessage () {
return `${this.$t('label.showing.results.for').replace('%x', this.searchQuery)}`
}
},
watch: {
apiParams () {
this.onSearch()
}
},
emits: ['change-option-value', 'change-option'],
methods: {
async fetchItems () {
if (this.successiveFetches === 0 && this.loading) return
this.loading = true
const params = {
page: this.page,
pagesize: this.computedPageSize
}
if (this.searchQuery && this.searchQuery.length > 0) {
params.keyword = this.searchQuery
}
if (this.apiParams) {
Object.assign(params, this.apiParams)
}
if (this.showIcon) {
params.showicon = true
}
api(this.api, params).then(json => {
const response = json[this.api.toLowerCase() + 'response'] || {}
if (this.totalCount === null) {
this.totalCount = response.count || 0
}
const newOpts = response[this.resourceType] || []
const existingOptions = new Set(this.options.map(o => o[this.optionValueKey]))
newOpts.forEach(opt => {
if (!existingOptions.has(opt[this.optionValueKey])) {
this.options.push(opt)
}
})
this.page++
this.checkAndFetchPreselectedOption()
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
if (this.successiveFetches === 0) {
this.loading = false
}
})
},
checkAndFetchPreselectedOption () {
if (!this.preselectedOptionValue ||
(Array.isArray(this.preselectedOptionValue) && this.preselectedOptionValue.length === 0) ||
this.successiveFetches >= this.maxSuccessiveFetches) {
this.resetPreselectedOptionValue()
return
}
const matchValue = Array.isArray(this.preselectedOptionValue) ? this.preselectedOptionValue[0] : this.preselectedOptionValue
const match = this.options.find(entry => entry[this.optionValueKey] === matchValue)
if (!match) {
this.successiveFetches++
if (this.options.length < this.totalCount) {
this.fetchItems()
} else {
this.resetPreselectedOptionValue()
}
return
}
if (Array.isArray(this.preselectedOptionValue) && this.preselectedOptionValue.length > 1) {
this.preselectedOptionValue = this.preselectedOptionValue.filter(o => o !== match)
} else {
this.resetPreselectedOptionValue()
}
},
addDefaultOptionIfNeeded () {
if (this.defaultOption) {
this.options.push(this.defaultOption)
}
},
resetPreselectedOptionValue () {
this.preselectedOptionValue = null
this.successiveFetches = 0
},
onSearchTimed (value) {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.onSearch(value)
}, 500)
},
onSearch (value) {
this.searchQuery = value
this.page = 1
this.totalCount = null
this.options = []
if (!this.searchQuery) {
this.addDefaultOptionIfNeeded()
}
this.fetchItems()
},
onScroll (e) {
const nearBottom = e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 10
const hasMore = this.options.length < this.totalCount
if (nearBottom && hasMore && !this.loading) {
this.fetchItems()
}
},
onChange (value) {
this.resetPreselectedOptionValue()
this.$emit('change-option-value', value)
if (Array.isArray(value)) {
return
}
if (value === undefined || value == null) {
this.$emit('change-option', undefined)
return
}
const match = this.options.find(entry => entry[this.optionValueKey] === value)
if (match) {
this.$emit('change-option', match)
}
}
}
}
</script>
<style lang="less" scoped>
.select-list-footer {
margin: 4px 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -919,7 +919,7 @@ export default {
if (['listVirtualMachinesMetrics'].includes(this.apiName) && this.dataView) {
delete params.details
delete params.isvnf
params.details = 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp'
params.details = 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff'
}
this.loading = true

View File

@ -202,7 +202,7 @@ export default {
},
fetchZoneDetails () {
api('listZones', {
zoneid: this.resource.zoneid
id: this.resource.zoneid
}).then(response => {
const zone = response?.listzonesresponse?.zone || []
this.securityGroupsEnabled = zone?.[0]?.securitygroupsenabled || this.$store.getters.showSecurityGroups
@ -336,11 +336,9 @@ export default {
params.name = values.name
params.displayname = values.displayname
params.ostypeid = values.ostypeid
if (this.securityGroupsEnabled) {
if (values.securitygroupids) {
if (this.securityGroupsEnabled && Array.isArray(values.securitygroupids) && values.securitygroupids.length > 0) {
params.securitygroupids = values.securitygroupids
}
}
if (values.isdynamicallyscalable !== undefined) {
params.isdynamicallyscalable = values.isdynamicallyscalable
}

View File

@ -29,47 +29,27 @@
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.accountids.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.accountids"
mode="multiple"
:loading="accountLoading"
:placeholder="apiParams.accountids.description"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="(opt, optIndex) in accounts" :key="optIndex" :label="opt.name || opt.description">
<span>
<resource-icon v-if="opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
<global-outlined style="margin-right: 5px" />
{{ opt.name || opt.description }}
</span>
</a-select-option>
</a-select>
api="listAccounts"
:apiParams="accountsApiParams"
resourceType="account"
defaultIcon="team-outlined" />
</a-form-item>
<a-form-item v-if="isAdminOrDomainAdmin()" name="projectids" ref="projectids">
<template #label>
<tooltip-label :title="$t('label.project')" :tooltip="apiParams.projectids.description"/>
</template>
<a-select
<infinite-scroll-select
v-model:value="form.projectids"
mode="multiple"
:loading="projectLoading"
:placeholder="apiParams.projectids.description"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="(opt, optIndex) in projects" :key="optIndex" :label="opt.name || opt.description">
<span>
<resource-icon v-if="opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
<global-outlined style="margin-right: 5px" />
{{ opt.name || opt.description }}
</span>
</a-select-option>
</a-select>
api="listProjects"
:apiParams="projectsApiParams"
resourceType="project"
defaultIcon="project-outlined" />
</a-form-item>
<a-form-item v-if="!isAdminOrDomainAdmin()">
<template #label>
@ -106,12 +86,14 @@ import { isAdminOrDomainAdmin } from '@/role'
import { ref, reactive, toRaw } from 'vue'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect'
export default {
name: 'CreateNetworkPermissions',
components: {
TooltipLabel,
ResourceIcon
ResourceIcon,
InfiniteScrollSelect
},
props: {
resource: {
@ -121,11 +103,7 @@ export default {
},
data () {
return {
loading: false,
accountLoading: false,
projectLoading: false,
accounts: [],
projects: []
loading: false
}
},
created () {
@ -133,45 +111,24 @@ export default {
this.form = reactive({})
this.rules = reactive({})
this.apiParams = this.$getApiParams('createNetworkPermissions')
this.fetchData()
},
computed: {
accountsApiParams () {
return {
details: 'min',
domainid: this.resource.domainid
}
},
projectsApiParams () {
return {
details: 'min'
}
}
},
methods: {
isAdminOrDomainAdmin () {
return isAdminOrDomainAdmin()
},
async fetchData () {
this.fetchAccountData()
this.fetchProjectData()
},
fetchAccountData () {
this.accounts = []
const params = {}
params.showicon = true
params.details = 'min'
params.domainid = this.resource.domainid
this.accountLoading = true
api('listAccounts', params).then(json => {
const listaccounts = json.listaccountsresponse.account || []
this.accounts = listaccounts
}).finally(() => {
this.accountLoading = false
})
},
fetchProjectData () {
this.projects = []
const params = {}
params.listall = true
params.showicon = true
params.details = 'min'
params.domainid = this.resource.domainid
this.projectLoading = true
api('listProjects', params).then(json => {
const listProjects = json.listprojectsresponse.project || []
this.projects = listProjects
}).finally(() => {
this.projectLoading = false
})
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
@ -179,31 +136,12 @@ export default {
const values = toRaw(this.form)
const params = {}
params.networkid = this.resource.id
var accountIndexes = values.accountids
var accountId = null
if (accountIndexes && accountIndexes.length > 0) {
var accountIds = []
for (var i = 0; i < accountIndexes.length; i++) {
accountIds = accountIds.concat(this.accounts[accountIndexes[i]].id)
if (values.accountids && values.accountids.length > 0) {
params.accountids = values.accountids.join(',')
}
accountId = accountIds.join(',')
if (values.projectids && values.projectids.length > 0) {
params.projectids = values.projectids.join(',')
}
if (accountId) {
params.accountids = accountId
}
var projectIndexes = values.projectids
var projectId = null
if (projectIndexes && projectIndexes.length > 0) {
var projectIds = []
for (var j = 0; j < projectIndexes.length; j++) {
projectIds = projectIds.concat(this.projects[projectIndexes[j]].id)
}
projectId = projectIds.join(',')
}
if (projectId) {
params.projectids = projectId
}
if (values.accounts && values.accounts.length > 0) {
params.accounts = values.accounts
}

View File

@ -1316,6 +1316,31 @@ export default {
this.fetchInstances()
}
},
fetchVmwareInstanceForKVMMigration (vmname, hostname) {
const params = {}
if (this.isMigrateFromVmware && this.selectedVmwareVcenter) {
if (this.selectedVmwareVcenter.vcenter) {
params.datacentername = this.selectedVmwareVcenter.datacentername
params.vcenter = this.selectedVmwareVcenter.vcenter
params.username = this.selectedVmwareVcenter.username
params.password = this.selectedVmwareVcenter.password
} else {
params.existingvcenterid = this.selectedVmwareVcenter.existingvcenterid
}
params.instancename = vmname
params.hostname = hostname
}
api('listVmwareDcVms', params).then(json => {
const response = json.listvmwaredcvmsresponse
this.selectedUnmanagedInstance = response.unmanagedinstance[0]
this.selectedUnmanagedInstance.ostypename = this.selectedUnmanagedInstance.osdisplayname
this.selectedUnmanagedInstance.state = this.selectedUnmanagedInstance.powerstate
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.loading = false
})
},
onManageInstanceAction () {
this.selectedUnmanagedInstance = {}
if (this.unmanagedInstances.length > 0 &&
@ -1333,6 +1358,9 @@ export default {
}
})
this.showUnmanageForm = false
} else if (this.isMigrateFromVmware) {
this.fetchVmwareInstanceForKVMMigration(this.selectedUnmanagedInstance.name, this.selectedUnmanagedInstance.hostname)
this.showUnmanageForm = true
} else {
this.showUnmanageForm = true
}

View File

@ -227,6 +227,8 @@ export default {
} else {
params.existingvcenterid = this.selectedExistingVcenterId
}
params.page = 1
params.pagesize = 10
api('listVmwareDcVms', params).then(json => {
const obj = {
params: params,
@ -265,6 +267,11 @@ export default {
this.loading = false
})
},
onSelectExternalVmwareDatacenter (value) {
if (this.vcenterSelectedOption === 'new' && !(this.vcenter === '' || this.datacentername === '' || this.username === '' || this.password === '')) {
this.listVmwareDatacenterVms()
}
},
onSelectExistingVmwareDatacenter (value) {
this.selectedExistingVcenterId = value
},

View File

@ -50,6 +50,7 @@ import org.joda.time.Duration;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.Pair;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.StringUtils;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.script.OutputInterpreter.TimedOutLogger;
@ -157,25 +158,15 @@ public class Script implements Callable<String> {
boolean obscureParam = false;
for (int i = 0; i < command.length; i++) {
String cmd = command[i];
if (obscureParam) {
builder.append("******").append(" ");
obscureParam = false;
if (StringUtils.isNotEmpty(cmd) && cmd.startsWith("vi://")) {
String[] tokens = cmd.split("@");
if (tokens.length >= 2) {
builder.append("vi://").append("******@").append(tokens[1]).append(" ");
} else {
builder.append(command[i]).append(" ");
builder.append("vi://").append("******").append(" ");
}
if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true;
_passwordCommand = true;
continue;
}
}
return builder.toString();
}
protected String buildCommandLine(List<String> command) {
StringBuilder builder = new StringBuilder();
boolean obscureParam = false;
for (String cmd : command) {
if (obscureParam) {
builder.append("******").append(" ");
obscureParam = false;

View File

@ -20,17 +20,35 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.utils.Pair;
import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.VirtualMachineBootOptions;
import com.vmware.vim25.VirtualMachinePowerState;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.apache.commons.lang3.StringUtils;
import com.vmware.vim25.CustomFieldDef;
import com.vmware.vim25.CustomFieldStringValue;
import com.vmware.vim25.ManagedObjectReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BaseMO {
protected static Logger logger = LogManager.getLogger(BaseMO.class);
protected VmwareContext _context;
protected ManagedObjectReference _mor;
protected static String[] propertyPathsForUnmanagedVmsThinListing = new String[] {"name", "config.template",
"runtime.powerState", "config.guestId", "config.guestFullName", "runtime.host",
"config.bootOptions", "config.firmware"};
private String _name;
public BaseMO(VmwareContext context, ManagedObjectReference mor) {
@ -154,4 +172,93 @@ public class BaseMO {
return cfmMo.getCustomFieldKey(morType, fieldName);
}
private static UnmanagedInstanceTO.PowerState convertPowerState(VirtualMachinePowerState powerState) {
return powerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn :
powerState == VirtualMachinePowerState.POWERED_OFF ? UnmanagedInstanceTO.PowerState.PowerOff : UnmanagedInstanceTO.PowerState.PowerUnknown;
}
protected List<UnmanagedInstanceTO> convertVmsObjectContentsToUnmanagedInstances(List<ObjectContent> ocs, String keyword) throws Exception {
Map<String, Pair<String, String>> hostClusterNamesMap = new HashMap<>();
List<UnmanagedInstanceTO> vms = new ArrayList<>();
if (ocs != null) {
for (ObjectContent oc : ocs) {
List<DynamicProperty> objProps = oc.getPropSet();
if (objProps != null) {
UnmanagedInstanceTO vm = createUnmanagedInstanceTOFromThinListingDynamicProperties(
objProps, keyword, hostClusterNamesMap);
if (vm != null) {
vms.add(vm);
}
}
}
}
if (vms.size() > 0) {
vms.sort(Comparator.comparing(UnmanagedInstanceTO::getName));
}
return vms;
}
private UnmanagedInstanceTO createUnmanagedInstanceTOFromThinListingDynamicProperties(List<DynamicProperty> objProps,
String keyword,
Map<String, Pair<String, String>> hostClusterNamesMap) throws Exception {
UnmanagedInstanceTO vm = new UnmanagedInstanceTO();
String vmName;
boolean isTemplate = false;
boolean excludeByKeyword = false;
for (DynamicProperty objProp : objProps) {
if (objProp.getName().equals("name")) {
vmName = (String) objProp.getVal();
if (StringUtils.isNotBlank(keyword) && !vmName.contains(keyword)) {
excludeByKeyword = true;
}
vm.setName(vmName);
} else if (objProp.getName().equals("config.template")) {
isTemplate = (Boolean) objProp.getVal();
} else if (objProp.getName().equals("runtime.powerState")) {
VirtualMachinePowerState powerState = (VirtualMachinePowerState) objProp.getVal();
vm.setPowerState(convertPowerState(powerState));
} else if (objProp.getName().equals("config.guestFullName")) {
vm.setOperatingSystem((String) objProp.getVal());
} else if (objProp.getName().equals("config.guestId")) {
vm.setOperatingSystemId((String) objProp.getVal());
} else if (objProp.getName().equals("config.bootOptions")) {
VirtualMachineBootOptions bootOptions = (VirtualMachineBootOptions) objProp.getVal();
String bootMode = "LEGACY";
if (bootOptions != null && bootOptions.isEfiSecureBootEnabled()) {
bootMode = "SECURE";
}
vm.setBootMode(bootMode);
} else if (objProp.getName().equals("config.firmware")) {
String firmware = (String) objProp.getVal();
vm.setBootType(firmware.equalsIgnoreCase("efi") ? "UEFI" : "BIOS");
} else if (objProp.getName().equals("runtime.host")) {
ManagedObjectReference hostMor = (ManagedObjectReference) objProp.getVal();
setUnmanagedInstanceTOHostAndCluster(vm, hostMor, hostClusterNamesMap);
}
}
if (isTemplate || excludeByKeyword) {
return null;
}
return vm;
}
private void setUnmanagedInstanceTOHostAndCluster(UnmanagedInstanceTO vm, ManagedObjectReference hostMor,
Map<String, Pair<String, String>> hostClusterNamesMap) throws Exception {
if (hostMor != null && StringUtils.isNotBlank(hostMor.getValue())) {
String hostMorValue = hostMor.getValue();
Pair<String, String> hostClusterPair;
if (hostClusterNamesMap.containsKey(hostMorValue)) {
hostClusterPair = hostClusterNamesMap.get(hostMorValue);
} else {
HostMO hostMO = new HostMO(_context, hostMor);
ClusterMO clusterMO = new ClusterMO(_context, hostMO.getHyperHostCluster());
hostClusterPair = new Pair<>(hostMO.getHostName(), clusterMO.getName());
hostClusterNamesMap.put(hostMorValue, hostClusterPair);
}
vm.setHostName(hostClusterPair.first());
vm.setClusterName(hostClusterPair.second());
}
}
}

View File

@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.apache.commons.collections.CollectionUtils;
@ -98,7 +97,7 @@ public class DatacenterMO extends BaseMO {
int key = cfmMo.getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_UUID);
assert (key != 0);
List<VirtualMachineMO> list = new ArrayList<VirtualMachineMO>();
List<VirtualMachineMO> list = new ArrayList<>();
List<ObjectContent> ocs = getVmPropertiesOnDatacenterVmFolder(new String[] {"name", String.format("value[%d]", key)});
if (ocs != null && ocs.size() > 0) {
@ -159,28 +158,9 @@ public class DatacenterMO extends BaseMO {
return null;
}
public List<UnmanagedInstanceTO> getAllVmsOnDatacenter() throws Exception {
List<UnmanagedInstanceTO> vms = new ArrayList<>();
List<ObjectContent> ocs = getVmPropertiesOnDatacenterVmFolder(new String[] {"name"});
if (ocs != null) {
for (ObjectContent oc : ocs) {
ManagedObjectReference vmMor = oc.getObj();
if (vmMor != null) {
VirtualMachineMO vmMo = new VirtualMachineMO(_context, vmMor);
try {
if (!vmMo.isTemplate()) {
HostMO hostMO = vmMo.getRunningHost();
UnmanagedInstanceTO unmanagedInstance = VmwareHelper.getUnmanagedInstance(hostMO, vmMo);
vms.add(unmanagedInstance);
}
} catch (Exception e) {
logger.debug(String.format("Unexpected error checking unmanaged instance %s, excluding it: %s", vmMo.getVmName(), e.getMessage()), e);
}
}
}
}
return vms;
public List<UnmanagedInstanceTO> getAllVmsOnDatacenter(String keyword) throws Exception {
List<ObjectContent> ocs = getVmPropertiesOnDatacenterVmFolder(propertyPathsForUnmanagedVmsThinListing);
return convertVmsObjectContentsToUnmanagedInstances(ocs, keyword);
}
public List<HostMO> getAllHostsOnDatacenter() throws Exception {
@ -275,7 +255,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
return _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -301,7 +281,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
return _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -336,7 +316,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
return _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -364,7 +344,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = context.getService().retrieveProperties(context.getPropertyCollector(), pfSpecArr);
@ -375,7 +355,7 @@ public class DatacenterMO extends BaseMO {
assert (ocs.get(0).getPropSet().get(0).getVal() != null);
String dcName = ocs.get(0).getPropSet().get(0).getVal().toString();
return new Pair<DatacenterMO, String>(new DatacenterMO(context, ocs.get(0).getObj()), dcName);
return new Pair<>(new DatacenterMO(context, ocs.get(0).getObj()), dcName);
}
public ManagedObjectReference getDvPortGroupMor(String dvPortGroupName) throws Exception {
@ -396,7 +376,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -443,7 +423,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -490,7 +470,7 @@ public class DatacenterMO extends BaseMO {
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);

View File

@ -2333,7 +2333,7 @@ public class VirtualMachineMO extends BaseMO {
vmdkDescriptor = getVmdkFileInfo(fileItem.first());
logger.info("Move VM disk file " + srcFile.getPath() + " to " + destFile.getPath());
srcDsMo.moveDatastoreFile(fileItem.first(), dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
moveDatastoreFile(srcDsMo, fileItem.first(), dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
if (vmdkDescriptor != null) {
String vmdkBaseFileName = vmdkDescriptor.first().getBaseFileName();
@ -2341,13 +2341,38 @@ public class VirtualMachineMO extends BaseMO {
destFile = new DatastoreFile(destDsMo.getName(), destDsDir, vmdkBaseFileName);
logger.info("Move VM disk file " + baseFilePath + " to " + destFile.getPath());
srcDsMo.moveDatastoreFile(baseFilePath, dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
moveDatastoreFile(srcDsMo, baseFilePath, dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
}
}
}
}
}
private boolean moveDatastoreFile(final DatastoreMO dsMo, String srcFilePath, ManagedObjectReference morSrcDc, ManagedObjectReference morDestDs,
String destFilePath, ManagedObjectReference morDestDc, boolean forceOverwrite) throws Exception {
final int retry = 20;
int retryAttempt = 0;
while (++retryAttempt <= retry) {
try {
logger.debug(String.format("Move datastore file %s, attempt #%d", srcFilePath, retryAttempt));
return dsMo.moveDatastoreFile(srcFilePath, morSrcDc, morDestDs, destFilePath, morDestDc, forceOverwrite);
} catch (Exception e) {
logger.info(String.format("Got exception while moving datastore file %s ", srcFilePath), e);
if (e.getMessage() != null && e.getMessage().contains("Unable to access file")) {
logger.debug(String.format("Failed to move datastore file %s. Retrying", srcFilePath));
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
logger.debug(String.format("Waiting to move datastore file %s been interrupted: ", srcFilePath));
}
} else {
throw e;
}
}
}
return false;
}
public int getPvScsiDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");

View File

@ -59,6 +59,8 @@ import com.vmware.vim25.NasDatastoreInfo;
import com.vmware.vim25.VMwareDVSPortSetting;
import com.vmware.vim25.VirtualDeviceFileBackingInfo;
import com.vmware.vim25.VirtualIDEController;
import com.vmware.vim25.VirtualMachineBootOptions;
import com.vmware.vim25.VirtualMachineConfigInfo;
import com.vmware.vim25.VirtualMachineConfigSummary;
import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
import com.vmware.vim25.VirtualMachineToolsStatus;
@ -811,6 +813,17 @@ public class VmwareHelper {
instance.setCpuSpeed(configSummary.getCpuReservation());
instance.setMemory(configSummary.getMemorySizeMB());
}
VirtualMachineConfigInfo configInfo = vmMo.getConfigInfo();
if (configInfo != null) {
String firmware = configInfo.getFirmware();
instance.setBootType(firmware.equalsIgnoreCase("efi") ? "UEFI" : "BIOS");
VirtualMachineBootOptions bootOptions = configInfo.getBootOptions();
String bootMode = "LEGACY";
if (bootOptions != null && bootOptions.isEfiSecureBootEnabled()) {
bootMode = "SECURE";
}
instance.setBootMode(bootMode);
}
try {
ClusterMO clusterMo = new ClusterMO(hyperHost.getContext(), hyperHost.getHyperHostCluster());