mirror of https://github.com/apache/cloudstack.git
Merge branch '4.15' into split-connections
This commit is contained in:
commit
dad1269c5b
|
|
@ -59,6 +59,7 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity
|
|||
s_fsm.addTransition(Error, Event.ExpungeRequested, Expunging);
|
||||
s_fsm.addTransition(Expunging, Event.ExpungeRequested, Expunging);
|
||||
s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed);
|
||||
s_fsm.addTransition(Expunging, Event.OperationFailed, Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ public class ListUsageRecordsCmd extends BaseListCmd {
|
|||
@Parameter(name = ApiConstants.OLD_FORMAT, type = CommandType.BOOLEAN, description = "Flag to enable description rendered in old format which uses internal database IDs instead of UUIDs. False by default.")
|
||||
private Boolean oldFormat;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN,
|
||||
description = "Specify if usage records should be fetched recursively per domain. If an account id is passed, records will be limited to that account.",
|
||||
since = "4.15")
|
||||
private Boolean recursive = false;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -153,6 +158,10 @@ public class ListUsageRecordsCmd extends BaseListCmd {
|
|||
return oldFormat != null && oldFormat;
|
||||
}
|
||||
|
||||
public Boolean isRecursive() {
|
||||
return recursive;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// 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.api;
|
||||
|
||||
public class GetVmVncTicketAnswer extends Answer {
|
||||
|
||||
private String ticket;
|
||||
|
||||
public GetVmVncTicketAnswer(String ticket, boolean result, String details) {
|
||||
this.ticket = ticket;
|
||||
this.result = result;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public String getTicket() {
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// 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.api;
|
||||
|
||||
public class GetVmVncTicketCommand extends Command {
|
||||
|
||||
private String vmInternalName;
|
||||
|
||||
public GetVmVncTicketCommand(String vmInternalName) {
|
||||
this.vmInternalName = vmInternalName;
|
||||
}
|
||||
|
||||
public String getVmInternalName() {
|
||||
return this.vmInternalName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,10 +18,10 @@
|
|||
//
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
|
||||
/**
|
||||
* used to tell the agent to migrate a vm to a different primary storage pool.
|
||||
* It is for now only implemented on Vmware and is supposed to work irrespective of whether the VM is started or not.
|
||||
|
|
@ -32,6 +32,7 @@ public class MigrateVmToPoolCommand extends Command {
|
|||
private String vmName;
|
||||
private String destinationPool;
|
||||
private boolean executeInSequence = false;
|
||||
private String hostGuidInTargetCluster;
|
||||
|
||||
protected MigrateVmToPoolCommand() {
|
||||
}
|
||||
|
|
@ -41,15 +42,22 @@ public class MigrateVmToPoolCommand extends Command {
|
|||
* @param vmName the name of the VM to migrate
|
||||
* @param volumes used to supply feedback on vmware generated names
|
||||
* @param destinationPool the primary storage pool to migrate the VM to
|
||||
* @param hostGuidInTargetCluster GUID of host in target cluster when migrating across clusters
|
||||
* @param executeInSequence
|
||||
*/
|
||||
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool, boolean executeInSequence) {
|
||||
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool,
|
||||
String hostGuidInTargetCluster, boolean executeInSequence) {
|
||||
this.vmName = vmName;
|
||||
this.volumes = volumes;
|
||||
this.destinationPool = destinationPool;
|
||||
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
|
||||
this.executeInSequence = executeInSequence;
|
||||
}
|
||||
|
||||
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool, boolean executeInSequence) {
|
||||
this(vmName, volumes, destinationPool, null, executeInSequence);
|
||||
}
|
||||
|
||||
public Collection<VolumeTO> getVolumes() {
|
||||
return volumes;
|
||||
}
|
||||
|
|
@ -62,6 +70,10 @@ public class MigrateVmToPoolCommand extends Command {
|
|||
return vmName;
|
||||
}
|
||||
|
||||
public String getHostGuidInTargetCluster() {
|
||||
return hostGuidInTargetCluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return executeInSequence;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class MigrateVolumeCommand extends Command {
|
|||
StorageFilerTO sourcePool;
|
||||
String attachedVmName;
|
||||
Volume.Type volumeType;
|
||||
private String hostGuidInTargetCluster;
|
||||
|
||||
private DataTO srcData;
|
||||
private DataTO destData;
|
||||
|
|
@ -54,9 +55,10 @@ public class MigrateVolumeCommand extends Command {
|
|||
this.setWait(timeout);
|
||||
}
|
||||
|
||||
public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool sourcePool, StoragePool targetPool) {
|
||||
public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool sourcePool, StoragePool targetPool, String hostGuidInTargetCluster) {
|
||||
this(volumeId,volumePath,targetPool, null, Volume.Type.UNKNOWN, -1);
|
||||
this.sourcePool = new StorageFilerTO(sourcePool);
|
||||
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
|
||||
}
|
||||
|
||||
public MigrateVolumeCommand(DataTO srcData, DataTO destData, Map<String, String> srcDetails, Map<String, String> destDetails, int timeout) {
|
||||
|
|
@ -101,6 +103,10 @@ public class MigrateVolumeCommand extends Command {
|
|||
return volumeType;
|
||||
}
|
||||
|
||||
public String getHostGuidInTargetCluster() {
|
||||
return hostGuidInTargetCluster;
|
||||
}
|
||||
|
||||
public DataTO getSrcData() {
|
||||
return srcData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
|
||||
|
|
@ -142,6 +141,7 @@ import com.cloud.deploy.DeploymentPlan;
|
|||
import com.cloud.deploy.DeploymentPlanner;
|
||||
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||
import com.cloud.deploy.DeploymentPlanningManager;
|
||||
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.event.UsageEventVO;
|
||||
|
|
@ -2210,7 +2210,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
}
|
||||
|
||||
private Answer[] attemptHypervisorMigration(StoragePool destPool, VMInstanceVO vm) {
|
||||
private Answer[] attemptHypervisorMigration(StoragePool destPool, VMInstanceVO vm, Long hostId) {
|
||||
if (hostId == null) {
|
||||
return null;
|
||||
}
|
||||
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
|
||||
// OfflineVmwareMigration: in case of vmware call vcenter to do it for us.
|
||||
// OfflineVmwareMigration: should we check the proximity of source and destination
|
||||
|
|
@ -2218,15 +2221,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
// OfflineVmwareMigration: we are checking on success to optionally delete an old vm if we are not
|
||||
List<Command> commandsToSend = hvGuru.finalizeMigrate(vm, destPool);
|
||||
|
||||
Long hostId = vm.getHostId();
|
||||
// OfflineVmwareMigration: probably this is null when vm is stopped
|
||||
if(hostId == null) {
|
||||
hostId = vm.getLastHostId();
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.isNotEmpty(commandsToSend)) {
|
||||
Commands commandsContainer = new Commands(Command.OnError.Stop);
|
||||
commandsContainer.addCommands(commandsToSend);
|
||||
|
|
@ -2241,7 +2235,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
return null;
|
||||
}
|
||||
|
||||
private void afterHypervisorMigrationCleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId, Answer[] hypervisorMigrationResults) throws InsufficientCapacityException {
|
||||
private void afterHypervisorMigrationCleanup(StoragePool destPool, VMInstanceVO vm, Long srcClusterId, Answer[] hypervisorMigrationResults) throws InsufficientCapacityException {
|
||||
boolean isDebugEnabled = s_logger.isDebugEnabled();
|
||||
if(isDebugEnabled) {
|
||||
String msg = String.format("cleaning up after hypervisor pool migration volumes for VM %s(%s) to pool %s(%s)", vm.getInstanceName(), vm.getUuid(), destPool.getName(), destPool.getUuid());
|
||||
|
|
@ -2250,18 +2244,23 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
setDestinationPoolAndReallocateNetwork(destPool, vm);
|
||||
// OfflineVmwareMigration: don't set this to null or have another way to address the command; twice migrating will lead to an NPE
|
||||
Long destPodId = destPool.getPodId();
|
||||
Long vmPodId = vm.getPodIdToDeployIn();
|
||||
if (destPodId == null || ! destPodId.equals(vmPodId)) {
|
||||
|
||||
if (destPodId == null || !destPodId.equals(vm.getPodIdToDeployIn())) {
|
||||
if(isDebugEnabled) {
|
||||
String msg = String.format("resetting lasHost for VM %s(%s) as pod (%s) is no good.", vm.getInstanceName(), vm.getUuid(), destPodId);
|
||||
s_logger.debug(msg);
|
||||
}
|
||||
|
||||
vm.setLastHostId(null);
|
||||
vm.setPodIdToDeployIn(destPodId);
|
||||
// OfflineVmwareMigration: a consecutive migration will fail probably (no host not pod)
|
||||
}// else keep last host set for this vm
|
||||
markVolumesInPool(vm,destPool, hypervisorMigrationResults);
|
||||
} else if (srcClusterId != null && destPool.getClusterId() != null && !srcClusterId.equals(destPool.getClusterId())) {
|
||||
if(isDebugEnabled) {
|
||||
String msg = String.format("resetting lasHost for VM %s(%s) as cluster changed", vm.getInstanceName(), vm.getUuid());
|
||||
s_logger.debug(msg);
|
||||
}
|
||||
vm.setLastHostId(null);
|
||||
} // else keep last host set for this vm
|
||||
markVolumesInPool(vm, destPool, hypervisorMigrationResults);
|
||||
// OfflineVmwareMigration: deal with answers, if (hypervisorMigrationResults.length > 0)
|
||||
// OfflineVmwareMigration: iterate over the volumes for data updates
|
||||
}
|
||||
|
|
@ -2295,23 +2294,60 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
}
|
||||
|
||||
private Pair<Long, Long> findClusterAndHostIdForVm(VMInstanceVO vm) {
|
||||
Long hostId = vm.getHostId();
|
||||
Long clusterId = null;
|
||||
// OfflineVmwareMigration: probably this is null when vm is stopped
|
||||
if(hostId == null) {
|
||||
hostId = vm.getLastHostId();
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
|
||||
}
|
||||
}
|
||||
if (hostId == null) {
|
||||
List<VolumeVO> volumes = _volsDao.findByInstanceAndType(vm.getId(), Type.ROOT);
|
||||
if (CollectionUtils.isNotEmpty(volumes)) {
|
||||
for (VolumeVO rootVolume : volumes) {
|
||||
if (rootVolume.getPoolId() != null) {
|
||||
StoragePoolVO pool = _storagePoolDao.findById(rootVolume.getPoolId());
|
||||
if (pool != null && pool.getClusterId() != null) {
|
||||
clusterId = pool.getClusterId();
|
||||
List<HostVO> hosts = _hostDao.findHypervisorHostInCluster(pool.getClusterId());
|
||||
if (CollectionUtils.isNotEmpty(hosts)) {
|
||||
hostId = hosts.get(0).getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clusterId == null && hostId != null) {
|
||||
HostVO host = _hostDao.findById(hostId);
|
||||
if (host != null) {
|
||||
clusterId = host.getClusterId();
|
||||
}
|
||||
}
|
||||
return new Pair<>(clusterId, hostId);
|
||||
}
|
||||
|
||||
private void migrateThroughHypervisorOrStorage(StoragePool destPool, VMInstanceVO vm) throws StorageUnavailableException, InsufficientCapacityException {
|
||||
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
|
||||
final Long srchostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
|
||||
final HostVO srcHost = _hostDao.findById(srchostId);
|
||||
final Long srcClusterId = srcHost.getClusterId();
|
||||
Answer[] hypervisorMigrationResults = attemptHypervisorMigration(destPool, vm);
|
||||
Pair<Long, Long> vmClusterAndHost = findClusterAndHostIdForVm(vm);
|
||||
final Long sourceClusterId = vmClusterAndHost.first();
|
||||
final Long sourceHostId = vmClusterAndHost.second();
|
||||
Answer[] hypervisorMigrationResults = attemptHypervisorMigration(destPool, vm, sourceHostId);
|
||||
boolean migrationResult = false;
|
||||
if (hypervisorMigrationResults == null) {
|
||||
// OfflineVmwareMigration: if the HypervisorGuru can't do it, let the volume manager take care of it.
|
||||
migrationResult = volumeMgr.storageMigration(profile, destPool);
|
||||
if (migrationResult) {
|
||||
afterStorageMigrationCleanup(destPool, vm, srcHost, srcClusterId);
|
||||
afterStorageMigrationCleanup(destPool, vm, sourceHostId, sourceClusterId);
|
||||
} else {
|
||||
s_logger.debug("Storage migration failed");
|
||||
}
|
||||
} else {
|
||||
afterHypervisorMigrationCleanup(destPool, vm, srcHost, srcClusterId, hypervisorMigrationResults);
|
||||
afterHypervisorMigrationCleanup(destPool, vm, sourceClusterId, hypervisorMigrationResults);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2366,7 +2402,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
|
||||
|
||||
private void afterStorageMigrationCleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId) throws InsufficientCapacityException {
|
||||
private void afterStorageMigrationCleanup(StoragePool destPool, VMInstanceVO vm, Long srcHostId, Long srcClusterId) throws InsufficientCapacityException {
|
||||
setDestinationPoolAndReallocateNetwork(destPool, vm);
|
||||
|
||||
//when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool
|
||||
|
|
@ -2376,7 +2412,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
// If VM was cold migrated between clusters belonging to two different VMware DCs,
|
||||
// unregister the VM from the source host and cleanup the associated VM files.
|
||||
if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
|
||||
afterStorageMigrationVmwareVMcleanup(destPool, vm, srcHost, srcClusterId);
|
||||
afterStorageMigrationVmwareVMCleanup(destPool, vm, srcHostId, srcClusterId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2394,14 +2430,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
}
|
||||
|
||||
private void afterStorageMigrationVmwareVMcleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId) {
|
||||
private void afterStorageMigrationVmwareVMCleanup(StoragePool destPool, VMInstanceVO vm, Long srcHostId, Long srcClusterId) {
|
||||
// OfflineVmwareMigration: this should only happen on storage migration, else the guru would already have issued the command
|
||||
final Long destClusterId = destPool.getClusterId();
|
||||
if (srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId)) {
|
||||
if (srcHostId != null && srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId)) {
|
||||
final String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
|
||||
final String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
|
||||
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
|
||||
removeStaleVmFromSource(vm, srcHost);
|
||||
removeStaleVmFromSource(vm, _hostDao.findById(srcHostId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.cloud.storage.dao.VMTemplateDetailsDao;
|
|||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.vm.SecondaryStorageVmVO;
|
||||
import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.SecondaryStorageVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
|
|
@ -220,6 +221,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
UserVmDetailsDao userVmDetailsDao;
|
||||
@Inject
|
||||
private SecondaryStorageVmDao secondaryStorageVmDao;
|
||||
@Inject
|
||||
VolumeApiService _volumeApiService;
|
||||
|
||||
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
|
||||
protected List<StoragePoolAllocator> _storagePoolAllocators;
|
||||
|
|
@ -1038,6 +1041,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Detaching " + vol);
|
||||
}
|
||||
VMInstanceVO vm = _userVmDao.findById(vmId);
|
||||
if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
|
||||
_volumeApiService.detachVolumeViaDestroyVM(vmId, vol.getId());
|
||||
}
|
||||
_volsDao.detachVolume(vol.getId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
--;
|
||||
-- Schema upgrade from 4.15.0.0 to 4.15.1.0
|
||||
--;
|
||||
|
||||
-- Correct guest OS names
|
||||
UPDATE `cloud`.`guest_os` SET display_name='Fedora Linux (32 bit)' WHERE id=320;
|
||||
UPDATE `cloud`.`guest_os` SET display_name='Mandriva Linux (32 bit)' WHERE id=323;
|
||||
|
|
@ -60,3 +59,76 @@ INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_vers
|
|||
ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `ike_version` varchar(5) NOT NULL DEFAULT 'ike' COMMENT 'one of ike, ikev1, ikev2';
|
||||
ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `split_connections` int(1) NOT NULL DEFAULT 0;
|
||||
|
||||
-- Add support for VMware 7.0
|
||||
INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '7.0', 1024, 0, 59, 64, 1, 1);
|
||||
INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '7.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='6.7';
|
||||
|
||||
-- Add support for darwin19_64Guest from VMware 7.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (336, UUID(), 7, 'macOS 10.15 (64 bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0', 'darwin19_64Guest', 336, now(), 0);
|
||||
|
||||
-- Add support for debian11_64Guest from VMware 7.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (337, UUID(), 2, 'Debian GNU/Linux 11 (64-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0', 'debian11_64Guest', 337, now(), 0);
|
||||
|
||||
-- Add support for debian11Guest from VMware 7.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (338, UUID(), 2, 'Debian GNU/Linux 11 (32-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0', 'debian11Guest', 338, now(), 0);
|
||||
|
||||
-- Add support for windows2019srv_64Guest from VMware 7.0
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0', 'windows2019srv_64Guest', 276, now(), 0);
|
||||
|
||||
|
||||
-- Add support for VMware 7.0.1.0
|
||||
INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '7.0.1.0', 1024, 0, 59, 64, 1, 1);
|
||||
INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '7.0.1.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='7.0';
|
||||
|
||||
-- Add support for amazonlinux3_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (339, UUID(), 7, 'Amazon Linux 3 (64 bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'amazonlinux3_64Guest', 339, now(), 0);
|
||||
|
||||
-- Add support for asianux9_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (340, UUID(), 7, 'Asianux Server 9 (64 bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'asianux9_64Guest', 340, now(), 0);
|
||||
|
||||
-- Add support for centos9_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (341, UUID(), 1, 'CentOS 9', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'centos9_64Guest', 341, now(), 0);
|
||||
|
||||
-- Add support for darwin20_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (342, UUID(), 7, 'macOS 11 (64 bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'darwin20_64Guest', 342, now(), 0);
|
||||
|
||||
-- Add support for darwin21_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'darwin21_64Guest', 342, now(), 0);
|
||||
|
||||
-- Add support for freebsd13_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (343, UUID(), 9, 'FreeBSD 13 (64-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'freebsd13_64Guest', 343, now(), 0);
|
||||
|
||||
-- Add support for freebsd13Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (344, UUID(), 9, 'FreeBSD 13 (32-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'freebsd13Guest', 344, now(), 0);
|
||||
|
||||
-- Add support for oracleLinux9_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (345, UUID(), 3, 'Oracle Linux 9', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'oracleLinux9_64Guest', 345, now(), 0);
|
||||
|
||||
-- Add support for other5xLinux64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (346, UUID(), 2, 'Linux 5.x Kernel (64-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'other5xLinux64Guest', 346, now(), 0);
|
||||
|
||||
-- Add support for other5xLinuxGuest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (347, UUID(), 2, 'Linux 5.x Kernel (32-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'other5xLinuxGuest', 347, now(), 0);
|
||||
|
||||
-- Add support for rhel9_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (348, UUID(), 4, 'Red Hat Enterprise Linux 9.0', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'rhel9_64Guest', 348, now(), 0);
|
||||
|
||||
-- Add support for sles16_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (349, UUID(), 5, 'SUSE Linux Enterprise Server 16 (64-bit)', now());
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'sles16_64Guest', 349, now(), 0);
|
||||
|
||||
-- Add support for windows2019srvNext_64Guest from VMware 7.0.1.0
|
||||
INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'windows2019srvNext_64Guest', 276, now(), 0);
|
||||
|
|
|
|||
|
|
@ -230,11 +230,10 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
|
|||
} else {
|
||||
String errMsg = (answer == null) ? null : answer.getDetails();
|
||||
s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + errMsg);
|
||||
processAnswer(vmSnapshotVO, userVm, answer, hostId);
|
||||
throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + errMsg);
|
||||
}
|
||||
} catch (OperationTimedoutException e) {
|
||||
throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage());
|
||||
} catch (AgentUnavailableException e) {
|
||||
} catch (OperationTimedoutException | AgentUnavailableException e) {
|
||||
throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
@ -254,9 +253,13 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
|
|||
finalizeRevert(vmSnapshot, answer.getVolumeTOs());
|
||||
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded);
|
||||
} else if (as instanceof DeleteVMSnapshotAnswer) {
|
||||
DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer)as;
|
||||
finalizeDelete(vmSnapshot, answer.getVolumeTOs());
|
||||
vmSnapshotDao.remove(vmSnapshot.getId());
|
||||
if (as.getResult()) {
|
||||
DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) as;
|
||||
finalizeDelete(vmSnapshot, answer.getVolumeTOs());
|
||||
vmSnapshotDao.remove(vmSnapshot.getId());
|
||||
} else {
|
||||
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
// under the License.
|
||||
package com.cloud.hypervisor.guru;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -149,8 +151,6 @@ import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
|
|||
import com.vmware.vim25.VirtualMachineConfigSummary;
|
||||
import com.vmware.vim25.VirtualMachineRuntimeInfo;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Configurable {
|
||||
private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
|
||||
|
||||
|
|
@ -209,16 +209,43 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm), getClusterId(vm.getId()));
|
||||
}
|
||||
|
||||
long getClusterId(long vmId) {
|
||||
long clusterId;
|
||||
Long hostId;
|
||||
private Long getClusterIdFromVmVolume(long vmId) {
|
||||
Long clusterId = null;
|
||||
List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
|
||||
if (CollectionUtils.isNotEmpty(volumes)) {
|
||||
for (VolumeVO rootVolume : volumes) {
|
||||
if (rootVolume.getPoolId() != null) {
|
||||
StoragePoolVO pool = _storagePoolDao.findById(rootVolume.getPoolId());
|
||||
if (pool != null && pool.getClusterId() != null) {
|
||||
clusterId = pool.getClusterId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
hostId = _vmDao.findById(vmId).getHostId();
|
||||
if (hostId == null) {
|
||||
private Long getClusterId(long vmId) {
|
||||
Long clusterId = null;
|
||||
Long hostId = null;
|
||||
VMInstanceVO vm = _vmDao.findById(vmId);
|
||||
if (vm != null) {
|
||||
hostId = _vmDao.findById(vmId).getHostId();
|
||||
}
|
||||
if (vm != null && hostId == null) {
|
||||
// If VM is in stopped state then hostId would be undefined. Hence read last host's Id instead.
|
||||
hostId = _vmDao.findById(vmId).getLastHostId();
|
||||
}
|
||||
clusterId = _hostDao.findById(hostId).getClusterId();
|
||||
HostVO host = null;
|
||||
if (hostId != null) {
|
||||
host = _hostDao.findById(hostId);
|
||||
}
|
||||
if (host != null) {
|
||||
clusterId = host.getClusterId();
|
||||
} else {
|
||||
clusterId = getClusterIdFromVmVolume(vmId);
|
||||
}
|
||||
|
||||
return clusterId;
|
||||
}
|
||||
|
|
@ -418,9 +445,11 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
|
||||
@Override public Map<String, String> getClusterSettings(long vmId) {
|
||||
Map<String, String> details = new HashMap<String, String>();
|
||||
long clusterId = getClusterId(vmId);
|
||||
details.put(VmwareReserveCpu.key(), VmwareReserveCpu.valueIn(clusterId).toString());
|
||||
details.put(VmwareReserveMemory.key(), VmwareReserveMemory.valueIn(clusterId).toString());
|
||||
Long clusterId = getClusterId(vmId);
|
||||
if (clusterId != null) {
|
||||
details.put(VmwareReserveCpu.key(), VmwareReserveCpu.valueIn(clusterId).toString());
|
||||
details.put(VmwareReserveMemory.key(), VmwareReserveMemory.valueIn(clusterId).toString());
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
|
|
@ -1056,6 +1085,29 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
return null;
|
||||
}
|
||||
|
||||
private boolean isInterClusterMigration(Long srcClusterId, Long destClusterId) {
|
||||
return srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId);
|
||||
}
|
||||
|
||||
private String getHostGuidInTargetCluster(boolean isInterClusterMigration, Long destClusterId) {
|
||||
String hostGuidInTargetCluster = null;
|
||||
if (isInterClusterMigration) {
|
||||
Host hostInTargetCluster = null;
|
||||
// Without host vMotion might fail between non-shared storages with error similar to,
|
||||
// https://kb.vmware.com/s/article/1003795
|
||||
// As this is offline migration VM won't be started on this host
|
||||
List<HostVO> hosts = _hostDao.findHypervisorHostInCluster(destClusterId);
|
||||
if (CollectionUtils.isNotEmpty(hosts)) {
|
||||
hostInTargetCluster = hosts.get(0);
|
||||
}
|
||||
if (hostInTargetCluster == null) {
|
||||
throw new CloudRuntimeException("Migration failed, unable to find suitable target host for VM placement while migrating between storage pools of different clusters without shared storages");
|
||||
}
|
||||
hostGuidInTargetCluster = hostInTargetCluster.getGuid();
|
||||
}
|
||||
return hostGuidInTargetCluster;
|
||||
}
|
||||
|
||||
@Override public List<Command> finalizeMigrate(VirtualMachine vm, StoragePool destination) {
|
||||
List<Command> commands = new ArrayList<Command>();
|
||||
|
||||
|
|
@ -1066,14 +1118,16 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
|||
VolumeTO vol = new VolumeTO(volume, destination);
|
||||
vols.add(vol);
|
||||
}
|
||||
MigrateVmToPoolCommand migrateVmToPoolCommand = new MigrateVmToPoolCommand(vm.getInstanceName(), vols, destination.getUuid(), true);
|
||||
|
||||
final Long destClusterId = destination.getClusterId();
|
||||
final Long srcClusterId = getClusterId(vm.getId());
|
||||
final boolean isInterClusterMigration = isInterClusterMigration(destClusterId, srcClusterId);
|
||||
MigrateVmToPoolCommand migrateVmToPoolCommand = new MigrateVmToPoolCommand(vm.getInstanceName(),
|
||||
vols, destination.getUuid(), getHostGuidInTargetCluster(isInterClusterMigration, destClusterId), true);
|
||||
commands.add(migrateVmToPoolCommand);
|
||||
|
||||
// OfflineVmwareMigration: cleanup if needed
|
||||
final Long destClusterId = destination.getClusterId();
|
||||
final Long srcClusterId = getClusterId(vm.getId());
|
||||
|
||||
if (srcClusterId != null && destClusterId != null && !srcClusterId.equals(destClusterId)) {
|
||||
if (isInterClusterMigration) {
|
||||
final String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
|
||||
final String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
|
||||
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
// under the License.
|
||||
package com.cloud.hypervisor.vmware.resource;
|
||||
|
||||
import static com.cloud.utils.HumanReadableJson.getHumanReadableBytesJson;
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
|
@ -45,16 +48,18 @@ import java.util.stream.Collectors;
|
|||
import javax.naming.ConfigurationException;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DeployAsIsInfoTO;
|
||||
import com.cloud.agent.api.ValidateVcenterDetailsCommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.storage.configdrive.ConfigDrive;
|
||||
import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
|
@ -96,6 +101,8 @@ import com.cloud.agent.api.GetUnmanagedInstancesCommand;
|
|||
import com.cloud.agent.api.GetVmDiskStatsAnswer;
|
||||
import com.cloud.agent.api.GetVmDiskStatsCommand;
|
||||
import com.cloud.agent.api.GetVmIpAddressCommand;
|
||||
import com.cloud.agent.api.GetVmVncTicketCommand;
|
||||
import com.cloud.agent.api.GetVmVncTicketAnswer;
|
||||
import com.cloud.agent.api.GetVmNetworkStatsAnswer;
|
||||
import com.cloud.agent.api.GetVmNetworkStatsCommand;
|
||||
import com.cloud.agent.api.GetVmStatsAnswer;
|
||||
|
|
@ -162,6 +169,7 @@ import com.cloud.agent.api.UnregisterVMCommand;
|
|||
import com.cloud.agent.api.UpgradeSnapshotCommand;
|
||||
import com.cloud.agent.api.ValidateSnapshotAnswer;
|
||||
import com.cloud.agent.api.ValidateSnapshotCommand;
|
||||
import com.cloud.agent.api.ValidateVcenterDetailsCommand;
|
||||
import com.cloud.agent.api.VmDiskStatsEntry;
|
||||
import com.cloud.agent.api.VmStatsEntry;
|
||||
import com.cloud.agent.api.VolumeStatsEntry;
|
||||
|
|
@ -178,12 +186,13 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
|||
import com.cloud.agent.api.storage.DestroyCommand;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeCommand;
|
||||
import com.cloud.agent.api.to.deployasis.OVFPropertyTO;
|
||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
|
||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
||||
import com.cloud.agent.api.storage.ResizeVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.ResizeVolumeCommand;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DeployAsIsInfoTO;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
import com.cloud.agent.api.to.IpAddressTO;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
|
|
@ -191,6 +200,7 @@ import com.cloud.agent.api.to.NicTO;
|
|||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.agent.api.to.deployasis.OVFPropertyTO;
|
||||
import com.cloud.agent.resource.virtualnetwork.VRScripts;
|
||||
import com.cloud.agent.resource.virtualnetwork.VirtualRouterDeployer;
|
||||
import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
|
||||
|
|
@ -219,8 +229,8 @@ import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
|
|||
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
|
||||
import com.cloud.hypervisor.vmware.mo.NetworkDetails;
|
||||
import com.cloud.hypervisor.vmware.mo.PbmProfileManagerMO;
|
||||
import com.cloud.hypervisor.vmware.mo.TaskMO;
|
||||
import com.cloud.hypervisor.vmware.mo.StoragepodMO;
|
||||
import com.cloud.hypervisor.vmware.mo.TaskMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
||||
|
|
@ -289,7 +299,6 @@ import com.vmware.vim25.HostInternetScsiHba;
|
|||
import com.vmware.vim25.HostPortGroupSpec;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
import com.vmware.vim25.NasDatastoreInfo;
|
||||
import com.vmware.vim25.VirtualMachineDefinedProfileSpec;
|
||||
import com.vmware.vim25.ObjectContent;
|
||||
import com.vmware.vim25.OptionValue;
|
||||
import com.vmware.vim25.PerfCounterInfo;
|
||||
|
|
@ -324,6 +333,7 @@ import com.vmware.vim25.VirtualEthernetCardOpaqueNetworkBackingInfo;
|
|||
import com.vmware.vim25.VirtualIDEController;
|
||||
import com.vmware.vim25.VirtualMachineBootOptions;
|
||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineDefinedProfileSpec;
|
||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||
import com.vmware.vim25.VirtualMachineFileLayoutEx;
|
||||
import com.vmware.vim25.VirtualMachineFileLayoutExFileInfo;
|
||||
|
|
@ -343,13 +353,6 @@ import com.vmware.vim25.VmConfigInfo;
|
|||
import com.vmware.vim25.VmConfigSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
|
||||
import static com.cloud.utils.HumanReadableJson.getHumanReadableBytesJson;
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService, VirtualRouterDeployer {
|
||||
private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
|
||||
|
|
@ -577,6 +580,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
answer = execute((PrepareUnmanageVMInstanceCommand) cmd);
|
||||
} else if (clz == ValidateVcenterDetailsCommand.class) {
|
||||
answer = execute((ValidateVcenterDetailsCommand) cmd);
|
||||
} else if (clz == GetVmVncTicketCommand.class) {
|
||||
answer = execute((GetVmVncTicketCommand) cmd);
|
||||
} else {
|
||||
answer = Answer.createUnsupportedCommandAnswer(cmd);
|
||||
}
|
||||
|
|
@ -2336,7 +2341,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
configBasicExtraOption(extraOptions, vmSpec);
|
||||
|
||||
if (deployAsIs) {
|
||||
setDeployAsIsProperties(vmMo, deployAsIsInfo, vmConfigSpec);
|
||||
setDeployAsIsProperties(vmMo, deployAsIsInfo, vmConfigSpec, hyperHost);
|
||||
}
|
||||
|
||||
configNvpExtraOption(extraOptions, vmSpec, nicUuidToDvSwitchUuid);
|
||||
|
|
@ -2563,12 +2568,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
* Set OVF properties (if available)
|
||||
*/
|
||||
private void setDeployAsIsProperties(VirtualMachineMO vmMo, DeployAsIsInfoTO deployAsIsInfo,
|
||||
VirtualMachineConfigSpec vmConfigSpec) throws Exception {
|
||||
if (deployAsIsInfo != null) {
|
||||
VirtualMachineConfigSpec vmConfigSpec, VmwareHypervisorHost hyperHost) throws Exception {
|
||||
if (deployAsIsInfo != null && MapUtils.isNotEmpty(deployAsIsInfo.getProperties())) {
|
||||
Map<String, String> properties = deployAsIsInfo.getProperties();
|
||||
VmConfigInfo vAppConfig = vmMo.getConfigInfo().getVAppConfig();
|
||||
s_logger.info("Copying OVF properties to the values the user provided");
|
||||
setVAppPropertiesToConfigSpec(vAppConfig, properties, vmConfigSpec);
|
||||
setVAppPropertiesToConfigSpec(vAppConfig, properties, vmConfigSpec, hyperHost);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2660,13 +2665,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
/**
|
||||
* Set the ovf section spec from existing vApp configuration
|
||||
*/
|
||||
protected List<VAppOvfSectionSpec> copyVAppConfigOvfSectionFromOVF(VmConfigInfo vAppConfig) {
|
||||
protected List<VAppOvfSectionSpec> copyVAppConfigOvfSectionFromOVF(VmConfigInfo vAppConfig, boolean useEdit) {
|
||||
List<VAppOvfSectionInfo> ovfSection = vAppConfig.getOvfSection();
|
||||
List<VAppOvfSectionSpec> specs = new ArrayList<>();
|
||||
for (VAppOvfSectionInfo info : ovfSection) {
|
||||
VAppOvfSectionSpec spec = new VAppOvfSectionSpec();
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
spec.setOperation(useEdit ? ArrayUpdateOperation.EDIT : ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
|
|
@ -2694,7 +2699,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
/**
|
||||
* Set the properties section from existing vApp configuration and values set on ovfProperties
|
||||
*/
|
||||
protected List<VAppPropertySpec> copyVAppConfigPropertySectionFromOVF(VmConfigInfo vAppConfig, Map<String, String> ovfProperties) {
|
||||
protected List<VAppPropertySpec> copyVAppConfigPropertySectionFromOVF(VmConfigInfo vAppConfig, Map<String, String> ovfProperties,
|
||||
boolean useEdit) {
|
||||
List<VAppPropertyInfo> productFromOvf = vAppConfig.getProperty();
|
||||
List<VAppPropertySpec> specs = new ArrayList<>();
|
||||
for (VAppPropertyInfo info : productFromOvf) {
|
||||
|
|
@ -2702,9 +2708,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
if (ovfProperties.containsKey(info.getId())) {
|
||||
String value = ovfProperties.get(info.getId());
|
||||
info.setValue(value);
|
||||
s_logger.info("Setting OVF property ID = " + info.getId() + " VALUE = " + value);
|
||||
}
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
spec.setOperation(useEdit ? ArrayUpdateOperation.EDIT : ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
|
|
@ -2713,13 +2720,14 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
/**
|
||||
* Set the product section spec from existing vApp configuration
|
||||
*/
|
||||
protected List<VAppProductSpec> copyVAppConfigProductSectionFromOVF(VmConfigInfo vAppConfig) {
|
||||
protected List<VAppProductSpec> copyVAppConfigProductSectionFromOVF(VmConfigInfo vAppConfig, boolean useEdit) {
|
||||
List<VAppProductInfo> productFromOvf = vAppConfig.getProduct();
|
||||
List<VAppProductSpec> specs = new ArrayList<>();
|
||||
for (VAppProductInfo info : productFromOvf) {
|
||||
VAppProductSpec spec = new VAppProductSpec();
|
||||
spec.setInfo(info);
|
||||
spec.setOperation(ArrayUpdateOperation.ADD);
|
||||
s_logger.info("Procuct info KEY " + info.getKey());
|
||||
spec.setOperation(useEdit ? ArrayUpdateOperation.EDIT : ArrayUpdateOperation.ADD);
|
||||
specs.add(spec);
|
||||
}
|
||||
return specs;
|
||||
|
|
@ -2731,16 +2739,19 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
*/
|
||||
protected void setVAppPropertiesToConfigSpec(VmConfigInfo vAppConfig,
|
||||
Map<String, String> ovfProperties,
|
||||
VirtualMachineConfigSpec vmConfig) throws Exception {
|
||||
VirtualMachineConfigSpec vmConfig, VmwareHypervisorHost hyperHost) throws Exception {
|
||||
VmConfigSpec vmConfigSpec = new VmConfigSpec();
|
||||
vmConfigSpec.getEula().addAll(vAppConfig.getEula());
|
||||
vmConfigSpec.setInstallBootStopDelay(vAppConfig.getInstallBootStopDelay());
|
||||
vmConfigSpec.setInstallBootRequired(vAppConfig.isInstallBootRequired());
|
||||
vmConfigSpec.setIpAssignment(vAppConfig.getIpAssignment());
|
||||
vmConfigSpec.getOvfEnvironmentTransport().addAll(vAppConfig.getOvfEnvironmentTransport());
|
||||
vmConfigSpec.getProduct().addAll(copyVAppConfigProductSectionFromOVF(vAppConfig));
|
||||
vmConfigSpec.getProperty().addAll(copyVAppConfigPropertySectionFromOVF(vAppConfig, ovfProperties));
|
||||
vmConfigSpec.getOvfSection().addAll(copyVAppConfigOvfSectionFromOVF(vAppConfig));
|
||||
|
||||
// For backward compatibility, prior to Vmware 6.5 use EDIT operation instead of ADD
|
||||
boolean useEditOperation = hyperHost.getContext().getServiceContent().getAbout().getApiVersion().compareTo("6.5") < 1;
|
||||
vmConfigSpec.getProduct().addAll(copyVAppConfigProductSectionFromOVF(vAppConfig, useEditOperation));
|
||||
vmConfigSpec.getProperty().addAll(copyVAppConfigPropertySectionFromOVF(vAppConfig, ovfProperties, useEditOperation));
|
||||
vmConfigSpec.getOvfSection().addAll(copyVAppConfigOvfSectionFromOVF(vAppConfig, useEditOperation));
|
||||
vmConfig.setVAppConfig(vmConfigSpec);
|
||||
}
|
||||
|
||||
|
|
@ -4391,6 +4402,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
final String vmName = cmd.getVmName();
|
||||
|
||||
VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
|
||||
VmwareHypervisorHost hyperHostInTargetCluster = null;
|
||||
if (cmd.getHostGuidInTargetCluster() != null) {
|
||||
hyperHostInTargetCluster = VmwareHelper.getHostMOFromHostName(getServiceContext(),
|
||||
cmd.getHostGuidInTargetCluster());
|
||||
}
|
||||
try {
|
||||
VirtualMachineMO vmMo = getVirtualMachineMO(vmName, hyperHost);
|
||||
if (vmMo == null) {
|
||||
|
|
@ -4400,7 +4416,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
}
|
||||
|
||||
String poolUuid = cmd.getDestinationPool();
|
||||
return migrateAndAnswer(vmMo, poolUuid, hyperHost, cmd);
|
||||
return migrateAndAnswer(vmMo, poolUuid, hyperHost, hyperHostInTargetCluster, cmd);
|
||||
} catch (Throwable e) { // hopefully only CloudRuntimeException :/
|
||||
if (e instanceof Exception) {
|
||||
return new Answer(cmd, (Exception) e);
|
||||
|
|
@ -4412,10 +4428,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
return new Answer(cmd, false, "unknown problem: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Answer migrateAndAnswer(VirtualMachineMO vmMo, String poolUuid, VmwareHypervisorHost hyperHost, Command cmd) throws Exception {
|
||||
ManagedObjectReference morDs = getTargetDatastoreMOReference(poolUuid, hyperHost);
|
||||
|
||||
private Answer migrateAndAnswer(VirtualMachineMO vmMo, String poolUuid,
|
||||
VmwareHypervisorHost sourceHyperHost, VmwareHypervisorHost targetHyperHost,
|
||||
Command cmd) throws Exception {
|
||||
ManagedObjectReference morDs = getTargetDatastoreMOReference(poolUuid, sourceHyperHost, targetHyperHost);
|
||||
try {
|
||||
// OfflineVmwareMigration: getVolumesFromCommand(cmd);
|
||||
Map<Integer, Long> volumeDeviceKey = getVolumesFromCommand(vmMo, cmd);
|
||||
|
|
@ -4424,7 +4440,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
s_logger.trace(String.format("disk to migrate has disk id %d and volumeId %d", diskId, volumeDeviceKey.get(diskId)));
|
||||
}
|
||||
}
|
||||
if (vmMo.changeDatastore(morDs)) {
|
||||
if (vmMo.changeDatastore(morDs, targetHyperHost)) {
|
||||
// OfflineVmwareMigration: create target specification to include in answer
|
||||
// Consolidate VM disks after successful VM migration
|
||||
// In case of a linked clone VM, if VM's disks are not consolidated, further VM operations such as volume snapshot, VM snapshot etc. will result in DB inconsistencies.
|
||||
|
|
@ -4500,18 +4516,28 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
volumeDeviceKey.put(diskId, volumeId);
|
||||
}
|
||||
|
||||
private ManagedObjectReference getTargetDatastoreMOReference(String destinationPool, VmwareHypervisorHost hyperHost) {
|
||||
private ManagedObjectReference getTargetDatastoreMOReference(String destinationPool,
|
||||
VmwareHypervisorHost hyperHost,
|
||||
VmwareHypervisorHost targetHyperHost) {
|
||||
ManagedObjectReference morDs;
|
||||
try {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(String.format("finding datastore %s", destinationPool));
|
||||
}
|
||||
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, destinationPool);
|
||||
if (morDs == null && targetHyperHost != null) {
|
||||
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(targetHyperHost, destinationPool);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String msg = "exception while finding data store " + destinationPool;
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg + ": " + e.getLocalizedMessage());
|
||||
}
|
||||
if (morDs == null) {
|
||||
String msg = String.format("Failed to find datastore for pool UUID: %s", destinationPool);
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
return morDs;
|
||||
}
|
||||
|
||||
|
|
@ -4627,7 +4653,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
morDc = srcHyperHost.getHyperHostDatacenter();
|
||||
morDcOfTargetHost = tgtHyperHost.getHyperHostDatacenter();
|
||||
if (!morDc.getValue().equalsIgnoreCase(morDcOfTargetHost.getValue())) {
|
||||
String msg = "Source host & target host are in different datacentesr";
|
||||
String msg = "Source host & target host are in different datacenter";
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
VmwareManager mgr = tgtHyperHost.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
|
|
@ -4839,6 +4865,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
String path = cmd.getVolumePath();
|
||||
|
||||
VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
|
||||
VmwareHypervisorHost hyperHostInTargetCluster = null;
|
||||
if (cmd.getHostGuidInTargetCluster() != null) {
|
||||
hyperHostInTargetCluster = VmwareHelper.getHostMOFromHostName(getServiceContext(), cmd.getHostGuidInTargetCluster());
|
||||
}
|
||||
VmwareHypervisorHost targetDSHost = hyperHostInTargetCluster != null ? hyperHostInTargetCluster : hyperHost;
|
||||
VirtualMachineMO vmMo = null;
|
||||
DatastoreMO dsMo = null;
|
||||
DatastoreMO destinationDsMo = null;
|
||||
|
|
@ -4854,8 +4885,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
// we need to spawn a worker VM to attach the volume to and move it
|
||||
morSourceDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getSourcePool().getUuid());
|
||||
dsMo = new DatastoreMO(hyperHost.getContext(), morSourceDS);
|
||||
morDestintionDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getTargetPool().getUuid());
|
||||
destinationDsMo = new DatastoreMO(hyperHost.getContext(), morDestintionDS);
|
||||
morDestintionDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(targetDSHost, cmd.getTargetPool().getUuid());
|
||||
destinationDsMo = new DatastoreMO(targetDSHost.getContext(), morDestintionDS);
|
||||
|
||||
vmName = getWorkerName(getServiceContext(), cmd, 0, dsMo);
|
||||
if (destinationDsMo.getDatastoreType().equalsIgnoreCase("VVOL")) {
|
||||
|
|
@ -4870,7 +4901,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
|
||||
s_logger.info("Create worker VM " + vmName);
|
||||
// OfflineVmwareMigration: 2. create the worker with access to the data(store)
|
||||
vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, vmName, null);
|
||||
vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, vmName,
|
||||
HypervisorHostHelper.getMinimumHostHardwareVersion(hyperHost, hyperHostInTargetCluster));
|
||||
if (vmMo == null) {
|
||||
// OfflineVmwareMigration: don't throw a general Exception but think of a specific one
|
||||
throw new CloudRuntimeException("Unable to create a worker VM for volume operation");
|
||||
|
|
@ -4934,7 +4966,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
}
|
||||
|
||||
// OfflineVmwareMigration: this may have to be disected and executed in separate steps
|
||||
answer = migrateAndAnswer(vmMo, cmd.getTargetPool().getUuid(), hyperHost, cmd);
|
||||
answer = migrateAndAnswer(vmMo, cmd.getTargetPool().getUuid(), hyperHost, hyperHostInTargetCluster, cmd);
|
||||
} catch (Exception e) {
|
||||
String msg = String.format("Migration of volume '%s' failed due to %s", cmd.getVolumePath(), e.getLocalizedMessage());
|
||||
s_logger.error(msg, e);
|
||||
|
|
@ -4943,9 +4975,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
try {
|
||||
// OfflineVmwareMigration: worker *may* have been renamed
|
||||
vmName = vmMo.getVmName();
|
||||
morSourceDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getTargetPool().getUuid());
|
||||
dsMo = new DatastoreMO(hyperHost.getContext(), morSourceDS);
|
||||
s_logger.info("Dettaching disks before destroying worker VM '" + vmName + "' after volume migration");
|
||||
morSourceDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(targetDSHost, cmd.getTargetPool().getUuid());
|
||||
dsMo = new DatastoreMO(targetDSHost.getContext(), morSourceDS);
|
||||
s_logger.info("Detaching disks before destroying worker VM '" + vmName + "' after volume migration");
|
||||
VirtualDisk[] disks = vmMo.getAllDiskDevice();
|
||||
String format = "disk %d was migrated to %s";
|
||||
for (VirtualDisk disk : disks) {
|
||||
|
|
@ -7534,4 +7566,25 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
return new Answer(cmd, false, "Provided vCenter server address is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
public String acquireVirtualMachineVncTicket(String vmInternalCSName) throws Exception {
|
||||
VmwareContext context = getServiceContext();
|
||||
VmwareHypervisorHost hyperHost = getHyperHost(context);
|
||||
DatacenterMO dcMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter());
|
||||
VirtualMachineMO vmMo = dcMo.findVm(vmInternalCSName);
|
||||
return vmMo.acquireVncTicket();
|
||||
}
|
||||
|
||||
private GetVmVncTicketAnswer execute(GetVmVncTicketCommand cmd) {
|
||||
String vmInternalName = cmd.getVmInternalName();
|
||||
s_logger.info("Getting VNC ticket for VM " + vmInternalName);
|
||||
try {
|
||||
String ticket = acquireVirtualMachineVncTicket(vmInternalName);
|
||||
boolean result = StringUtils.isNotBlank(ticket);
|
||||
return new GetVmVncTicketAnswer(ticket, result, result ? "" : "Empty ticket obtained");
|
||||
} catch (Exception e) {
|
||||
s_logger.error("Error getting VNC ticket for VM " + vmInternalName, e);
|
||||
return new GetVmVncTicketAnswer(null, false, e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,21 @@ import java.util.Map;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.MigrateWithStorageAnswer;
|
||||
|
|
@ -53,18 +68,6 @@ import com.cloud.utils.Pair;
|
|||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
||||
|
|
@ -84,22 +87,19 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
@Override
|
||||
public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
|
||||
// OfflineVmwareMigration: return StrategyPriority.HYPERVISOR when destData is in a storage pool in the same vmware-cluster and both are volumes
|
||||
// OfflineVmwareMigration: return StrategyPriority.HYPERVISOR when destData is in a storage pool in the same pod or one of srcData & destData is in a zone-wide pool and both are volumes
|
||||
if (isOnVmware(srcData, destData)
|
||||
&& isOnPrimary(srcData, destData)
|
||||
&& isVolumesOnly(srcData, destData)
|
||||
&& isDettached(srcData)
|
||||
&& isIntraCluster(srcData, destData)
|
||||
&& isStoreScopeEqual(srcData, destData)) {
|
||||
&& isIntraPodOrZoneWideStoreInvolved(srcData, destData)
|
||||
&& isDettached(srcData)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
String msg = String.format("%s can handle the request because %d(%s) and %d(%s) share the VMware cluster %s (== %s)"
|
||||
String msg = String.format("%s can handle the request because %d(%s) and %d(%s) share the pod"
|
||||
, this.getClass()
|
||||
, srcData.getId()
|
||||
, srcData.getUuid()
|
||||
, destData.getId()
|
||||
, destData.getUuid()
|
||||
, storagePoolDao.findById(srcData.getDataStore().getId()).getClusterId()
|
||||
, storagePoolDao.findById(destData.getDataStore().getId()).getClusterId());
|
||||
, destData.getUuid());
|
||||
s_logger.debug(msg);
|
||||
}
|
||||
return StrategyPriority.HYPERVISOR;
|
||||
|
|
@ -107,6 +107,17 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
private boolean isIntraPodOrZoneWideStoreInvolved(DataObject srcData, DataObject destData) {
|
||||
DataStore srcStore = srcData.getDataStore();
|
||||
StoragePoolVO srcPool = storagePoolDao.findById(srcStore.getId());
|
||||
DataStore destStore = destData.getDataStore();
|
||||
StoragePoolVO destPool = storagePoolDao.findById(destStore.getId());
|
||||
if (srcPool.getPodId() != null && destPool.getPodId() != null) {
|
||||
return srcPool.getPodId().equals(destPool.getPodId());
|
||||
}
|
||||
return (ScopeType.ZONE.equals(srcPool.getScope()) || ScopeType.ZONE.equals(destPool.getScope()));
|
||||
}
|
||||
|
||||
private boolean isDettached(DataObject srcData) {
|
||||
VolumeVO volume = volDao.findById(srcData.getId());
|
||||
return volume.getInstanceId() == null;
|
||||
|
|
@ -127,30 +138,37 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
&& HypervisorType.VMware.equals(destData.getTO().getHypervisorType());
|
||||
}
|
||||
|
||||
private boolean isIntraCluster(DataObject srcData, DataObject destData) {
|
||||
DataStore srcStore = srcData.getDataStore();
|
||||
StoragePool srcPool = storagePoolDao.findById(srcStore.getId());
|
||||
DataStore destStore = destData.getDataStore();
|
||||
StoragePool destPool = storagePoolDao.findById(destStore.getId());
|
||||
if (srcPool.getClusterId() != null && destPool.getClusterId() != null) {
|
||||
return srcPool.getClusterId().equals(destPool.getClusterId());
|
||||
private Pair<Long, String> getHostIdForVmAndHostGuidInTargetCluster(DataObject srcData, DataObject destData) {
|
||||
StoragePool sourcePool = (StoragePool) srcData.getDataStore();
|
||||
ScopeType sourceScopeType = srcData.getDataStore().getScope().getScopeType();
|
||||
StoragePool targetPool = (StoragePool) destData.getDataStore();
|
||||
ScopeType targetScopeType = destData.getDataStore().getScope().getScopeType();
|
||||
Long hostId = null;
|
||||
String hostGuidInTargetCluster = null;
|
||||
if (ScopeType.CLUSTER.equals(sourceScopeType)) {
|
||||
// Find Volume source cluster and select any Vmware hypervisor host to attach worker VM
|
||||
hostId = findSuitableHostIdForWorkerVmPlacement(sourcePool.getClusterId());
|
||||
if (hostId == null) {
|
||||
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable host for worker VM placement in the cluster of storage pool: " + sourcePool.getName());
|
||||
}
|
||||
if (ScopeType.CLUSTER.equals(targetScopeType) && !sourcePool.getClusterId().equals(targetPool.getClusterId())) {
|
||||
// Without host vMotion might fail between non-shared storages with error similar to,
|
||||
// https://kb.vmware.com/s/article/1003795
|
||||
List<HostVO> hosts = hostDao.findHypervisorHostInCluster(targetPool.getClusterId());
|
||||
if (CollectionUtils.isNotEmpty(hosts)) {
|
||||
hostGuidInTargetCluster = hosts.get(0).getGuid();
|
||||
}
|
||||
if (hostGuidInTargetCluster == null) {
|
||||
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable target host for worker VM placement while migrating between storage pools of different cluster without shared storages");
|
||||
}
|
||||
}
|
||||
} else if (ScopeType.CLUSTER.equals(targetScopeType)) {
|
||||
hostId = findSuitableHostIdForWorkerVmPlacement(targetPool.getClusterId());
|
||||
if (hostId == null) {
|
||||
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable host for worker VM placement in the cluster of storage pool: " + targetPool.getName());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the scope of source and destination storage pools match
|
||||
*
|
||||
* @param srcData
|
||||
* @param destData
|
||||
* @return
|
||||
*/
|
||||
private boolean isStoreScopeEqual(DataObject srcData, DataObject destData) {
|
||||
DataStore srcStore = srcData.getDataStore();
|
||||
DataStore destStore = destData.getDataStore();
|
||||
String msg = String.format("Storage scope of source pool is %s and of destination pool is %s", srcStore.getScope().toString(), destStore.getScope().toString());
|
||||
s_logger.debug(msg);
|
||||
return srcStore.getScope().getScopeType() == (destStore.getScope().getScopeType());
|
||||
return new Pair<>(hostId, hostGuidInTargetCluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -187,21 +205,19 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
// OfflineVmwareMigration: we shouldn't be here as we would have refused in the canHandle call
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
Pair<Long, String> hostIdForVmAndHostGuidInTargetCluster = getHostIdForVmAndHostGuidInTargetCluster(srcData, destData);
|
||||
Long hostId = hostIdForVmAndHostGuidInTargetCluster.first();
|
||||
String hostGuidInTargetCluster = hostIdForVmAndHostGuidInTargetCluster.second();
|
||||
StoragePool sourcePool = (StoragePool) srcData.getDataStore();
|
||||
StoragePool targetPool = (StoragePool) destData.getDataStore();
|
||||
MigrateVolumeCommand cmd = new MigrateVolumeCommand(srcData.getId()
|
||||
, srcData.getTO().getPath()
|
||||
, sourcePool
|
||||
, targetPool);
|
||||
, targetPool
|
||||
, hostGuidInTargetCluster);
|
||||
// OfflineVmwareMigration: should be ((StoragePool)srcData.getDataStore()).getHypervisor() but that is NULL, so hardcoding
|
||||
Answer answer;
|
||||
ScopeType scopeType = srcData.getDataStore().getScope().getScopeType();
|
||||
if (ScopeType.CLUSTER == scopeType) {
|
||||
// Find Volume source cluster and select any Vmware hypervisor host to attach worker VM
|
||||
Long hostId = findSuitableHostIdForWorkerVmPlacement(sourcePool.getClusterId());
|
||||
if (hostId == null) {
|
||||
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable host for worker VM placement in cluster: " + sourcePool.getName());
|
||||
}
|
||||
if (hostId != null) {
|
||||
answer = agentMgr.easySend(hostId, cmd);
|
||||
} else {
|
||||
answer = agentMgr.sendTo(sourcePool.getDataCenterId(), HypervisorType.VMware, cmd);
|
||||
|
|
|
|||
|
|
@ -21,10 +21,14 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.component.ComponentLifecycleBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.LdapValidator;
|
||||
import org.apache.cloudstack.api.command.LDAPConfigCmd;
|
||||
|
|
@ -42,6 +46,8 @@ import org.apache.cloudstack.api.response.LdapConfigurationResponse;
|
|||
import org.apache.cloudstack.api.response.LdapUserResponse;
|
||||
import org.apache.cloudstack.api.response.LinkAccountToLdapResponse;
|
||||
import org.apache.cloudstack.api.response.LinkDomainToLdapResponse;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
|
||||
import org.apache.cloudstack.ldap.dao.LdapConfigurationDao;
|
||||
import org.apache.cloudstack.ldap.dao.LdapTrustMapDao;
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
|
@ -57,7 +63,7 @@ import com.cloud.user.dao.AccountDao;
|
|||
import com.cloud.utils.Pair;
|
||||
|
||||
@Component
|
||||
public class LdapManagerImpl implements LdapManager, LdapValidator {
|
||||
public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManager, LdapValidator {
|
||||
private static final Logger LOGGER = Logger.getLogger(LdapManagerImpl.class.getName());
|
||||
|
||||
@Inject
|
||||
|
|
@ -80,6 +86,9 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||
@Inject
|
||||
LdapTrustMapDao _ldapTrustMapDao;
|
||||
|
||||
@Inject
|
||||
private MessageBus messageBus;
|
||||
|
||||
public LdapManagerImpl() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -93,6 +102,33 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||
_ldapConfiguration = ldapConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
super.configure(name, params);
|
||||
LOGGER.debug("Configuring LDAP Manager");
|
||||
|
||||
messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, new MessageSubscriber() {
|
||||
@Override
|
||||
public void onPublishMessage(String senderAddress, String subject, Object args) {
|
||||
try {
|
||||
final Account account = accountDao.findByIdIncludingRemoved((Long) args);
|
||||
long domainId = account.getDomainId();
|
||||
LdapTrustMapVO ldapTrustMapVO = _ldapTrustMapDao.findByAccount(domainId, account.getAccountId());
|
||||
if (ldapTrustMapVO != null) {
|
||||
String msg = String.format("Removing link between LDAP: %s - type: %s and account: %s on domain: %s",
|
||||
ldapTrustMapVO.getName(), ldapTrustMapVO.getType().name(), account.getAccountId(), domainId);
|
||||
LOGGER.debug(msg);
|
||||
_ldapTrustMapDao.remove(ldapTrustMapVO.getId());
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error("Caught exception while removing account linked to LDAP", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException {
|
||||
return addConfigurationInternal(cmd.getHostname(),cmd.getPort(),cmd.getDomainId());
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -166,7 +166,7 @@
|
|||
<cs.servlet.version>4.0.1</cs.servlet.version>
|
||||
<cs.tomcat-embed-core.version>8.5.61</cs.tomcat-embed-core.version>
|
||||
<cs.trilead.version>build-217-jenkins-27</cs.trilead.version>
|
||||
<cs.vmware.api.version>6.7</cs.vmware.api.version>
|
||||
<cs.vmware.api.version>7.0</cs.vmware.api.version>
|
||||
<cs.winrm4j.version>0.5.0</cs.winrm4j.version>
|
||||
<cs.xapi.version>6.2.0-3.1</cs.xapi.version>
|
||||
<cs.xmlrpc.version>3.1.3</cs.xmlrpc.version>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class ConsoleProxyClientParam {
|
|||
private String password;
|
||||
|
||||
private String sourceIP;
|
||||
private String websocketUrl;
|
||||
|
||||
public ConsoleProxyClientParam() {
|
||||
clientHostPort = 0;
|
||||
|
|
@ -150,4 +151,12 @@ public class ConsoleProxyClientParam {
|
|||
public void setSourceIP(String sourceIP) {
|
||||
this.sourceIP = sourceIP;
|
||||
}
|
||||
|
||||
public String getWebsocketUrl() {
|
||||
return websocketUrl;
|
||||
}
|
||||
|
||||
public void setWebsocketUrl(String websocketUrl) {
|
||||
this.websocketUrl = websocketUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.GetVmVncTicketAnswer;
|
||||
import com.cloud.agent.api.GetVmVncTicketCommand;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import org.apache.cloudstack.framework.security.keys.KeysManager;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.log4j.Logger;
|
||||
|
|
@ -94,6 +101,8 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
UserVmDetailsDao _userVmDetailsDao;
|
||||
@Inject
|
||||
KeysManager _keysMgr;
|
||||
@Inject
|
||||
AgentManager agentManager;
|
||||
|
||||
static KeysManager s_keysMgr;
|
||||
|
||||
|
|
@ -427,6 +436,47 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL to establish a VNC over websocket connection
|
||||
*/
|
||||
private void setWebsocketUrl(VirtualMachine vm, ConsoleProxyClientParam param) {
|
||||
String ticket = acquireVncTicketForVmwareVm(vm);
|
||||
if (StringUtils.isBlank(ticket)) {
|
||||
s_logger.error("Could not obtain VNC ticket for VM " + vm.getInstanceName());
|
||||
return;
|
||||
}
|
||||
String wsUrl = composeWebsocketUrlForVmwareVm(ticket, param);
|
||||
param.setWebsocketUrl(wsUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format expected: wss://<ESXi_HOST_IP>:443/ticket/<TICKET_ID>
|
||||
*/
|
||||
private String composeWebsocketUrlForVmwareVm(String ticket, ConsoleProxyClientParam param) {
|
||||
param.setClientHostPort(443);
|
||||
return String.format("wss://%s:%s/ticket/%s", param.getClientHostAddress(), param.getClientHostPort(), ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a ticket to be used for console proxy as described in 'Removal of VNC Server from ESXi' on:
|
||||
* https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-esxi-vcenter-server-70-release-notes.html
|
||||
*/
|
||||
private String acquireVncTicketForVmwareVm(VirtualMachine vm) {
|
||||
try {
|
||||
s_logger.info("Acquiring VNC ticket for VM = " + vm.getHostName());
|
||||
GetVmVncTicketCommand cmd = new GetVmVncTicketCommand(vm.getInstanceName());
|
||||
Answer answer = agentManager.send(vm.getHostId(), cmd);
|
||||
GetVmVncTicketAnswer ans = (GetVmVncTicketAnswer) answer;
|
||||
if (!ans.getResult()) {
|
||||
s_logger.info("VNC ticket could not be acquired correctly: " + ans.getDetails());
|
||||
}
|
||||
return ans.getTicket();
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
s_logger.error("Error acquiring ticket", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO hostVo, InetAddress addr) {
|
||||
StringBuffer sb = new StringBuffer(rootUrl);
|
||||
String host = hostVo.getPrivateIpAddress();
|
||||
|
|
@ -477,6 +527,10 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
param.setTicket(ticket);
|
||||
param.setSourceIP(addr != null ? addr.getHostAddress(): null);
|
||||
|
||||
if (requiresVncOverWebSocketConnection(vm, hostVo)) {
|
||||
setWebsocketUrl(vm, param);
|
||||
}
|
||||
|
||||
if (details != null) {
|
||||
param.setLocale(details.getValue());
|
||||
}
|
||||
|
|
@ -513,6 +567,14 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since VMware 7.0 VNC servers are deprecated, it uses a ticket to create a VNC over websocket connection
|
||||
* Check: https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-esxi-vcenter-server-70-release-notes.html
|
||||
*/
|
||||
private boolean requiresVncOverWebSocketConnection(VirtualMachine vm, HostVO hostVo) {
|
||||
return vm.getHypervisorType() == Hypervisor.HypervisorType.VMware && hostVo.getHypervisorVersion().compareTo("7.0") >= 0;
|
||||
}
|
||||
|
||||
public static String genAccessTicket(String host, String port, String sid, String tag) {
|
||||
return genAccessTicket(host, port, sid, tag, new Date());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ import com.cloud.vm.VmWorkResizeVolume;
|
|||
import com.cloud.vm.VmWorkSerializer;
|
||||
import com.cloud.vm.VmWorkTakeVolumeSnapshot;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
|
|
@ -219,6 +220,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
@Inject
|
||||
private UserVmDao _userVmDao;
|
||||
@Inject
|
||||
private UserVmDetailsDao userVmDetailsDao;
|
||||
@Inject
|
||||
private UserVmService _userVmService;
|
||||
@Inject
|
||||
private VolumeDataStoreDao _volumeStoreDao;
|
||||
|
|
@ -906,9 +909,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId());
|
||||
|
||||
if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer && hypervisorType != HypervisorType.VMware && hypervisorType != HypervisorType.Any
|
||||
if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer
|
||||
&& hypervisorType != HypervisorType.VMware && hypervisorType != HypervisorType.Any
|
||||
&& hypervisorType != HypervisorType.None) {
|
||||
throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support rootdisksize override");
|
||||
throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support volume resize");
|
||||
}
|
||||
|
||||
if (volume.getState() != Volume.State.Ready && volume.getState() != Volume.State.Allocated) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.TimeZone;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.domain.Domain;
|
||||
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;
|
||||
|
|
@ -200,22 +201,41 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
|
|||
}
|
||||
}
|
||||
|
||||
boolean isAdmin = false;
|
||||
boolean isDomainAdmin = false;
|
||||
boolean ignoreAccountId = false;
|
||||
boolean isDomainAdmin = _accountService.isDomainAdmin(caller.getId());
|
||||
boolean isNormalUser = _accountService.isNormalUser(caller.getId());
|
||||
|
||||
//If accountId couldn't be found using accountName and domainId, get it from userContext
|
||||
if (accountId == null) {
|
||||
accountId = caller.getId();
|
||||
//List records for all the accounts if the caller account is of type admin.
|
||||
//If account_id or account_name is explicitly mentioned, list records for the specified account only even if the caller is of type admin
|
||||
if (_accountService.isRootAdmin(caller.getId())) {
|
||||
isAdmin = true;
|
||||
} else if (_accountService.isDomainAdmin(caller.getId())) {
|
||||
isDomainAdmin = true;
|
||||
}
|
||||
ignoreAccountId = _accountService.isRootAdmin(caller.getId());
|
||||
s_logger.debug("Account details not available. Using userContext accountId: " + accountId);
|
||||
}
|
||||
|
||||
// Check if a domain admin is allowed to access the requested domain id
|
||||
if (isDomainAdmin) {
|
||||
if (domainId != null) {
|
||||
Account callerAccount = _accountService.getAccount(caller.getId());
|
||||
Domain domain = _domainDao.findById(domainId);
|
||||
_accountService.checkAccess(callerAccount, domain);
|
||||
} else {
|
||||
// Domain admins can only access their own domain's usage records.
|
||||
// Set the domain if not specified.
|
||||
domainId = caller.getDomainId();
|
||||
}
|
||||
|
||||
if (cmd.getAccountId() != null) {
|
||||
// Check if a domain admin is allowed to access the requested account info.
|
||||
checkDomainAdminAccountAccess(accountId, domainId);
|
||||
}
|
||||
}
|
||||
|
||||
// By default users do not have access to this API.
|
||||
// Adding checks here in case someone changes the default access.
|
||||
checkUserAccess(cmd, accountId, caller, isNormalUser);
|
||||
|
||||
Date startDate = cmd.getStartDate();
|
||||
Date endDate = cmd.getEndDate();
|
||||
if (startDate.after(endDate)) {
|
||||
|
|
@ -234,22 +254,27 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
|
|||
|
||||
SearchCriteria<UsageVO> sc = _usageDao.createSearchCriteria();
|
||||
|
||||
if (accountId != -1 && accountId != Account.ACCOUNT_ID_SYSTEM && !isAdmin && !isDomainAdmin) {
|
||||
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
|
||||
}
|
||||
|
||||
if (isDomainAdmin) {
|
||||
SearchCriteria<DomainVO> sdc = _domainDao.createSearchCriteria();
|
||||
sdc.addOr("path", SearchCriteria.Op.LIKE, _domainDao.findById(caller.getDomainId()).getPath() + "%");
|
||||
List<DomainVO> domains = _domainDao.search(sdc, null);
|
||||
List<Long> domainIds = new ArrayList<Long>();
|
||||
for (DomainVO domain : domains)
|
||||
domainIds.add(domain.getId());
|
||||
sc.addAnd("domainId", SearchCriteria.Op.IN, domainIds.toArray());
|
||||
if (accountId != -1 && accountId != Account.ACCOUNT_ID_SYSTEM && !ignoreAccountId) {
|
||||
// account exists and either domain on user role
|
||||
// If not recursive and the account belongs to the user/domain admin, or the account was passed in, filter
|
||||
if ((accountId == caller.getId() && !cmd.isRecursive()) || cmd.getAccountId() != null){
|
||||
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
|
||||
}
|
||||
}
|
||||
|
||||
if (domainId != null) {
|
||||
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
|
||||
if (cmd.isRecursive()) {
|
||||
SearchCriteria<DomainVO> sdc = _domainDao.createSearchCriteria();
|
||||
sdc.addOr("path", SearchCriteria.Op.LIKE, _domainDao.findById(domainId).getPath() + "%");
|
||||
List<DomainVO> domains = _domainDao.search(sdc, null);
|
||||
List<Long> domainIds = new ArrayList<Long>();
|
||||
for (DomainVO domain : domains) {
|
||||
domainIds.add(domain.getId());
|
||||
}
|
||||
sc.addAnd("domainId", SearchCriteria.Op.IN, domainIds.toArray());
|
||||
} else {
|
||||
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
|
||||
}
|
||||
}
|
||||
|
||||
if (usageType != null) {
|
||||
|
|
@ -372,6 +397,46 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag
|
|||
return new Pair<List<? extends Usage>, Integer>(usageRecords.first(), usageRecords.second());
|
||||
}
|
||||
|
||||
private void checkUserAccess(ListUsageRecordsCmd cmd, Long accountId, Account caller, boolean isNormalUser) {
|
||||
if (isNormalUser) {
|
||||
// A user can only access their own account records
|
||||
if (caller.getId() != accountId) {
|
||||
throw new PermissionDeniedException("Users are only allowed to list usage records for their own account.");
|
||||
}
|
||||
// Users cannot get recursive records
|
||||
if (cmd.isRecursive()) {
|
||||
throw new PermissionDeniedException("Users are not allowed to list usage records recursively.");
|
||||
}
|
||||
// Users cannot get domain records
|
||||
if (cmd.getDomainId() != null) {
|
||||
throw new PermissionDeniedException("Users are not allowed to list usage records for a domain");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDomainAdminAccountAccess(Long accountId, Long domainId) {
|
||||
Account account = _accountService.getAccount(accountId);
|
||||
boolean matchFound = false;
|
||||
|
||||
if (account.getDomainId() == domainId) {
|
||||
matchFound = true;
|
||||
} else {
|
||||
|
||||
// Check if the account is in a child domain of this domain admin.
|
||||
List<DomainVO> childDomains = _domainDao.findAllChildren(_domainDao.findById(domainId).getPath(), domainId);
|
||||
|
||||
for (DomainVO domainVO : childDomains) {
|
||||
if (account.getDomainId() == domainVO.getId()) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matchFound) {
|
||||
throw new PermissionDeniedException("Domain admins may only retrieve usage records for accounts in their own domain and child domains.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZone getUsageTimezone() {
|
||||
return _usageTimezone;
|
||||
|
|
|
|||
|
|
@ -2968,13 +2968,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
stopVirtualMachine(vmId, VmDestroyForcestop.value());
|
||||
|
||||
if (vm.getHypervisorType() == HypervisorType.VMware) {
|
||||
List<VolumeVO> allVolumes = _volsDao.findByInstance(vm.getId());
|
||||
allVolumes.removeIf(vol -> vol.getVolumeType() == Volume.Type.ROOT);
|
||||
detachVolumesFromVm(allVolumes);
|
||||
} else {
|
||||
detachVolumesFromVm(volumesToBeDeleted);
|
||||
}
|
||||
detachVolumesFromVm(volumesToBeDeleted);
|
||||
|
||||
UserVm destroyedVm = destroyVm(vmId, expunge);
|
||||
if (expunge) {
|
||||
|
|
@ -6981,6 +6975,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
newVols.add(newVol);
|
||||
|
||||
if (userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !newVol.getSize().equals(template.getSize())) {
|
||||
VolumeVO resizedVolume = (VolumeVO) newVol;
|
||||
resizedVolume.setSize(template.getSize());
|
||||
_volsDao.update(resizedVolume.getId(), resizedVolume);
|
||||
}
|
||||
|
||||
// 1. Save usage event and update resource count for user vm volumes
|
||||
_resourceLimitMgr.incrementResourceCount(newVol.getAccountId(), ResourceType.volume, newVol.isDisplay());
|
||||
_resourceLimitMgr.incrementResourceCount(newVol.getAccountId(), ResourceType.primary_storage, newVol.isDisplay(), new Long(newVol.getSize()));
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@
|
|||
<artifactId>websocket-server</artifactId>
|
||||
<version>${cs.jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<version>1.5.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.util.Map;
|
|||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.cloud.utils.StringUtils;
|
||||
import org.apache.log4j.xml.DOMConfigurator;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
|
||||
|
|
@ -172,6 +173,11 @@ public class ConsoleProxy {
|
|||
authResult.setHost(param.getClientHostAddress());
|
||||
authResult.setPort(param.getClientHostPort());
|
||||
|
||||
String websocketUrl = param.getWebsocketUrl();
|
||||
if (StringUtils.isNotBlank(websocketUrl)) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (standaloneStart) {
|
||||
return authResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public class ConsoleProxyClientParam {
|
|||
private String hypervHost;
|
||||
private String username;
|
||||
private String password;
|
||||
private String websocketUrl;
|
||||
|
||||
private String sourceIP;
|
||||
|
||||
|
|
@ -153,4 +154,12 @@ public class ConsoleProxyClientParam {
|
|||
public void setSourceIP(String sourceIP) {
|
||||
this.sourceIP = sourceIP;
|
||||
}
|
||||
|
||||
public String getWebsocketUrl() {
|
||||
return websocketUrl;
|
||||
}
|
||||
|
||||
public void setWebsocketUrl(String websocketUrl) {
|
||||
this.websocketUrl = websocketUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ public class ConsoleProxyHttpHandlerHelper {
|
|||
map.put("password", param.getPassword());
|
||||
if (param.getSourceIP() != null)
|
||||
map.put("sourceIP", param.getSourceIP());
|
||||
if (param.getWebsocketUrl() != null) {
|
||||
map.put("websocketUrl", param.getWebsocketUrl());
|
||||
}
|
||||
} else {
|
||||
s_logger.error("Unable to decode token");
|
||||
}
|
||||
|
|
@ -116,5 +119,6 @@ public class ConsoleProxyHttpHandlerHelper {
|
|||
map.remove("hypervHost");
|
||||
map.remove("username");
|
||||
map.remove("password");
|
||||
map.remove("websocketUrl");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler {
|
|||
String username = queryMap.get("username");
|
||||
String password = queryMap.get("password");
|
||||
String sourceIP = queryMap.get("sourceIP");
|
||||
String websocketUrl = queryMap.get("websocketUrl");
|
||||
|
||||
if (tag == null)
|
||||
tag = "";
|
||||
|
|
@ -131,6 +132,7 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler {
|
|||
param.setHypervHost(hypervHost);
|
||||
param.setUsername(username);
|
||||
param.setPassword(password);
|
||||
param.setWebsocketUrl(websocketUrl);
|
||||
viewer = ConsoleProxy.getNoVncViewer(param, ajaxSessionIdStr, session);
|
||||
} catch (Exception e) {
|
||||
s_logger.warn("Failed to create viewer due to " + e.getMessage(), e);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// under the License.
|
||||
package com.cloud.consoleproxy;
|
||||
|
||||
import com.cloud.utils.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
|
|
@ -96,47 +97,30 @@ public class ConsoleProxyNoVncClient implements ConsoleProxyClient {
|
|||
|
||||
String tunnelUrl = param.getClientTunnelUrl();
|
||||
String tunnelSession = param.getClientTunnelSession();
|
||||
String websocketUrl = param.getWebsocketUrl();
|
||||
|
||||
try {
|
||||
if (tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null
|
||||
&& !tunnelSession.isEmpty()) {
|
||||
URI uri = new URI(tunnelUrl);
|
||||
s_logger.info("Connect to VNC server via tunnel. url: " + tunnelUrl + ", session: "
|
||||
+ tunnelSession);
|
||||
connectClientToVNCServer(tunnelUrl, tunnelSession, websocketUrl);
|
||||
|
||||
ConsoleProxy.ensureRoute(uri.getHost());
|
||||
client.connectTo(uri.getHost(), uri.getPort(), uri.getPath() + "?" + uri.getQuery(),
|
||||
tunnelSession, "https".equalsIgnoreCase(uri.getScheme()));
|
||||
} else {
|
||||
s_logger.info("Connect to VNC server directly. host: " + getClientHostAddress() + ", port: "
|
||||
+ getClientHostPort());
|
||||
ConsoleProxy.ensureRoute(getClientHostAddress());
|
||||
client.connectTo(getClientHostAddress(), getClientHostPort());
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
} catch (IOException e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
} catch (Throwable e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
}
|
||||
|
||||
String ver = client.handshake();
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(ver.getBytes(), 0, ver.length()));
|
||||
|
||||
byte[] b = client.authenticate(getClientHostPassword());
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, 4));
|
||||
authenticateToVNCServer();
|
||||
|
||||
int readBytes;
|
||||
byte[] b;
|
||||
while (connectionAlive) {
|
||||
b = new byte[100];
|
||||
readBytes = client.read(b);
|
||||
if (readBytes == -1) {
|
||||
break;
|
||||
}
|
||||
if (readBytes > 0) {
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, readBytes));
|
||||
updateFrontEndActivityTime();
|
||||
if (client.isVncOverWebSocketConnection()) {
|
||||
if (client.isVncOverWebSocketConnectionOpen()) {
|
||||
updateFrontEndActivityTime();
|
||||
}
|
||||
connectionAlive = client.isVncOverWebSocketConnectionAlive();
|
||||
} else {
|
||||
b = new byte[100];
|
||||
readBytes = client.read(b);
|
||||
if (readBytes == -1) {
|
||||
break;
|
||||
}
|
||||
if (readBytes > 0) {
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, readBytes));
|
||||
updateFrontEndActivityTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
connectionAlive = false;
|
||||
|
|
@ -149,6 +133,55 @@ public class ConsoleProxyNoVncClient implements ConsoleProxyClient {
|
|||
worker.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate to VNC server when not using websockets
|
||||
* @throws IOException
|
||||
*/
|
||||
private void authenticateToVNCServer() throws IOException {
|
||||
if (!client.isVncOverWebSocketConnection()) {
|
||||
String ver = client.handshake();
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(ver.getBytes(), 0, ver.length()));
|
||||
|
||||
byte[] b = client.authenticate(getClientHostPassword());
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, 4));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a VNC server in one of three possible ways:
|
||||
* - When tunnelUrl and tunnelSession are not empty -> via tunnel
|
||||
* - When websocketUrl is not empty -> connect to websocket
|
||||
* - Otherwise -> connect to TCP port on host directly
|
||||
*/
|
||||
private void connectClientToVNCServer(String tunnelUrl, String tunnelSession, String websocketUrl) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(websocketUrl)) {
|
||||
s_logger.info("Connect to VNC over websocket URL: " + websocketUrl);
|
||||
client.connectToWebSocket(websocketUrl, session);
|
||||
} else if (tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null
|
||||
&& !tunnelSession.isEmpty()) {
|
||||
URI uri = new URI(tunnelUrl);
|
||||
s_logger.info("Connect to VNC server via tunnel. url: " + tunnelUrl + ", session: "
|
||||
+ tunnelSession);
|
||||
|
||||
ConsoleProxy.ensureRoute(uri.getHost());
|
||||
client.connectTo(uri.getHost(), uri.getPort(), uri.getPath() + "?" + uri.getQuery(),
|
||||
tunnelSession, "https".equalsIgnoreCase(uri.getScheme()));
|
||||
} else {
|
||||
s_logger.info("Connect to VNC server directly. host: " + getClientHostAddress() + ", port: "
|
||||
+ getClientHostPort());
|
||||
ConsoleProxy.ensureRoute(getClientHostAddress());
|
||||
client.connectTo(getClientHostAddress(), getClientHostPort());
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
} catch (IOException e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
} catch (Throwable e) {
|
||||
s_logger.error("Unexpected exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setClientParam(ConsoleProxyClientParam param) {
|
||||
this.clientParam = param;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ import java.io.DataInputStream;
|
|||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.spec.KeySpec;
|
||||
|
||||
|
|
@ -31,6 +34,8 @@ import javax.crypto.spec.DESKeySpec;
|
|||
|
||||
import com.cloud.consoleproxy.util.Logger;
|
||||
import com.cloud.consoleproxy.util.RawHTTP;
|
||||
import com.cloud.consoleproxy.websocket.WebSocketReverseProxy;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
|
||||
public class NoVncClient {
|
||||
private static final Logger s_logger = Logger.getLogger(NoVncClient.class);
|
||||
|
|
@ -39,6 +44,8 @@ public class NoVncClient {
|
|||
private DataInputStream is;
|
||||
private DataOutputStream os;
|
||||
|
||||
private WebSocketReverseProxy webSocketReverseProxy;
|
||||
|
||||
public NoVncClient() {
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +69,30 @@ public class NoVncClient {
|
|||
setStreams();
|
||||
}
|
||||
|
||||
// VNC over WebSocket connection helpers
|
||||
public void connectToWebSocket(String websocketUrl, Session session) throws URISyntaxException {
|
||||
webSocketReverseProxy = new WebSocketReverseProxy(new URI(websocketUrl), session);
|
||||
webSocketReverseProxy.connect();
|
||||
}
|
||||
|
||||
public boolean isVncOverWebSocketConnection() {
|
||||
return webSocketReverseProxy != null;
|
||||
}
|
||||
|
||||
public boolean isVncOverWebSocketConnectionOpen() {
|
||||
return isVncOverWebSocketConnection() && webSocketReverseProxy.isOpen();
|
||||
}
|
||||
|
||||
public boolean isVncOverWebSocketConnectionAlive() {
|
||||
return isVncOverWebSocketConnection() && !webSocketReverseProxy.isClosing() && !webSocketReverseProxy.isClosed();
|
||||
}
|
||||
|
||||
public void proxyMsgOverWebSocketConnection(ByteBuffer msg) {
|
||||
if (isVncOverWebSocketConnection()) {
|
||||
webSocketReverseProxy.proxyMsgFromRemoteSessionToEndpoint(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStreams() throws IOException {
|
||||
this.is = new DataInputStream(this.socket.getInputStream());
|
||||
this.os = new DataOutputStream(this.socket.getOutputStream());
|
||||
|
|
@ -213,7 +244,11 @@ public class NoVncClient {
|
|||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
os.write(b);
|
||||
if (isVncOverWebSocketConnection()) {
|
||||
proxyMsgOverWebSocketConnection(ByteBuffer.wrap(b));
|
||||
} else {
|
||||
os.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// 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.consoleproxy.websocket;
|
||||
|
||||
import com.cloud.consoleproxy.util.Logger;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.drafts.Draft_6455;
|
||||
import org.java_websocket.extensions.DefaultExtension;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
import org.java_websocket.protocols.Protocol;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Acts as a websocket reverse proxy between the remoteSession and the connected endpoint
|
||||
* - Connects to a websocket endpoint and sends the received data to the remoteSession endpoint
|
||||
* - Receives data from the remoteSession through the receiveProxiedMsg() method and forwards it to the connected endpoint
|
||||
*
|
||||
* remoteSession WebSocketReverseProxy websocket endpoint
|
||||
* data -----------------> receiveProxiedMsg() -----------> data
|
||||
* data <----------------- onMessage() <------------------- data
|
||||
*/
|
||||
public class WebSocketReverseProxy extends WebSocketClient {
|
||||
|
||||
private static final Protocol protocol = new Protocol("binary");
|
||||
private static final DefaultExtension defaultExtension = new DefaultExtension();
|
||||
private static final Draft_6455 draft = new Draft_6455(Collections.singletonList(defaultExtension), Collections.singletonList(protocol));
|
||||
|
||||
private static final Logger logger = Logger.getLogger(WebSocketReverseProxy.class);
|
||||
private Session remoteSession;
|
||||
|
||||
private void acceptAllCerts() {
|
||||
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[]{};
|
||||
}
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
}};
|
||||
SSLContext sc;
|
||||
try {
|
||||
sc = SSLContext.getInstance("TLS");
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
SSLSocketFactory factory = sc.getSocketFactory();
|
||||
this.setSocketFactory(factory);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public WebSocketReverseProxy(URI wsUrl, Session session) {
|
||||
super(wsUrl, draft);
|
||||
this.remoteSession = session;
|
||||
acceptAllCerts();
|
||||
setConnectionLostTimeout(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake serverHandshake) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason, boolean remote) {
|
||||
logger.info("Closing connection to websocket: reason=" + reason + " code=" + code + " remote=" + remote);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
logger.error("Error on connection to websocket: " + ex.getLocalizedMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ByteBuffer bytes) {
|
||||
try {
|
||||
this.remoteSession.getRemote().sendBytes(bytes);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error proxing msg from websocket to client side: " + e.getLocalizedMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void proxyMsgFromRemoteSessionToEndpoint(ByteBuffer msg) {
|
||||
this.getConnection().send(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ COMMIT
|
|||
-A INPUT -i eth0 -p udp -m udp --dport 53 -j ACCEPT
|
||||
-A INPUT -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT
|
||||
-A INPUT -i eth1 -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 3922 -j ACCEPT
|
||||
-A INPUT -i eth0 -p tcp -m tcp -m state --state NEW --dport 80 -j ACCEPT
|
||||
-A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
-A FORWARD -i eth2 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
-A FORWARD -i eth0 -o eth0 -m state --state NEW -j ACCEPT
|
||||
|
|
|
|||
|
|
@ -418,6 +418,8 @@ class CsIP:
|
|||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 80 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
|
|
@ -467,9 +469,10 @@ class CsIP:
|
|||
["filter", "", "-A INPUT -i %s -p udp -m udp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 80 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(
|
||||
["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
|
||||
self.fw.append(["mangle", "",
|
||||
|
|
|
|||
|
|
@ -59,16 +59,6 @@ class CsApache(CsApp):
|
|||
file.commit()
|
||||
CsHelper.execute2("systemctl restart apache2", False)
|
||||
|
||||
self.fw.append([
|
||||
"", "front",
|
||||
"-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 80 -j ACCEPT" % (self.dev, self.ip)
|
||||
])
|
||||
|
||||
self.fw.append([
|
||||
"", "front",
|
||||
"-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 443 -j ACCEPT" % (self.dev, self.ip)
|
||||
])
|
||||
|
||||
|
||||
class CsPasswdSvc():
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -429,11 +429,11 @@ class TestRemoteDiagnostics(cloudstackTestCase):
|
|||
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
|
||||
def test_10_traceroute_in_vr(self):
|
||||
'''
|
||||
Test Arping command execution in VR
|
||||
Test traceroute command execution in VR
|
||||
'''
|
||||
|
||||
# Validate the following:
|
||||
# 1. Arping command is executed remotely on VR
|
||||
# 1. Traceroute command is executed remotely on VR
|
||||
|
||||
list_router_response = list_routers(
|
||||
self.apiclient,
|
||||
|
|
@ -452,13 +452,13 @@ class TestRemoteDiagnostics(cloudstackTestCase):
|
|||
cmd.targetid = router.id
|
||||
cmd.ipaddress = '8.8.4.4'
|
||||
cmd.type = 'traceroute'
|
||||
cmd.params = "-m 10"
|
||||
cmd.params = "-m 5"
|
||||
cmd_response = self.apiclient.runDiagnostics(cmd)
|
||||
|
||||
self.assertEqual(
|
||||
'0',
|
||||
cmd_response.exitcode,
|
||||
'Failed to run remote Arping in VR')
|
||||
'Failed to run remote Traceroute in VR')
|
||||
|
||||
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
|
||||
def test_11_traceroute_in_ssvm(self):
|
||||
|
|
@ -488,7 +488,7 @@ class TestRemoteDiagnostics(cloudstackTestCase):
|
|||
cmd.targetid = ssvm.id
|
||||
cmd.ipaddress = '8.8.4.4'
|
||||
cmd.type = 'traceroute'
|
||||
cmd.params = '-m 10'
|
||||
cmd.params = '-m 5'
|
||||
cmd_response = self.apiclient.runDiagnostics(cmd)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
@ -525,7 +525,7 @@ class TestRemoteDiagnostics(cloudstackTestCase):
|
|||
cmd.targetid = cpvm.id
|
||||
cmd.ipaddress = '8.8.4.4'
|
||||
cmd.type = 'traceroute'
|
||||
cmd.params = '-m 10'
|
||||
cmd.params = '-m 5'
|
||||
cmd_response = self.apiclient.runDiagnostics(cmd)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ from operator import itemgetter
|
|||
|
||||
_multiprocess_shared_ = True
|
||||
|
||||
|
||||
class TestDeployVM(cloudstackTestCase):
|
||||
|
||||
@classmethod
|
||||
|
|
@ -1409,6 +1408,11 @@ class TestKVMLiveMigration(cloudstackTestCase):
|
|||
if len(self.hosts) < 2:
|
||||
self.skipTest("Requires at least two hosts for performing migration related tests")
|
||||
|
||||
|
||||
for host in self.hosts:
|
||||
if host.details['Host.OS'] in ['CentOS']:
|
||||
self.skipTest("live migration is not stabily supported on CentOS")
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
|
|
@ -1745,9 +1749,6 @@ class TestVAppsVM(cloudstackTestCase):
|
|||
cls.l2_network_offering
|
||||
]
|
||||
|
||||
# Uncomment when tests are finished, to cleanup the test templates
|
||||
for template in cls.templates:
|
||||
cls._cleanup.append(template)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
|
@ -1769,21 +1770,21 @@ class TestVAppsVM(cloudstackTestCase):
|
|||
def get_ova_parsed_information_from_template(self, template):
|
||||
if not template:
|
||||
return None
|
||||
details = template.details.__dict__
|
||||
details = template.deployasisdetails.__dict__
|
||||
configurations = []
|
||||
disks = []
|
||||
isos = []
|
||||
networks = []
|
||||
for propKey in details:
|
||||
if propKey.startswith('ACS-configuration'):
|
||||
if propKey.startswith('configuration'):
|
||||
configurations.append(json.loads(details[propKey]))
|
||||
elif propKey.startswith('ACS-disk'):
|
||||
elif propKey.startswith('disk'):
|
||||
detail = json.loads(details[propKey])
|
||||
if detail['isIso'] == True:
|
||||
isos.append(detail)
|
||||
else:
|
||||
disks.append(detail)
|
||||
elif propKey.startswith('ACS-network'):
|
||||
elif propKey.startswith('network'):
|
||||
networks.append(json.loads(details[propKey]))
|
||||
|
||||
return configurations, disks, isos, networks
|
||||
|
|
@ -1813,32 +1814,6 @@ class TestVAppsVM(cloudstackTestCase):
|
|||
msg="VM NIC(InstanceID: {}) network mismatch, expected = {}, result = {}".format(nic_network["nic"], nic_network["network"], nic.networkid)
|
||||
)
|
||||
|
||||
def verify_disks(self, template_disks, vm_id):
|
||||
cmd = listVolumes.listVolumesCmd()
|
||||
cmd.virtualmachineid = vm_id
|
||||
cmd.listall = True
|
||||
vm_volumes = self.apiclient.listVolumes(cmd)
|
||||
self.assertEqual(
|
||||
isinstance(vm_volumes, list),
|
||||
True,
|
||||
"Check listVolumes response returns a valid list"
|
||||
)
|
||||
self.assertEqual(
|
||||
len(template_disks),
|
||||
len(vm_volumes),
|
||||
msg="VM volumes count is different, expected = {}, result = {}".format(len(template_disks), len(vm_volumes))
|
||||
)
|
||||
template_disks.sort(key=itemgetter('diskNumber'))
|
||||
vm_volumes.sort(key=itemgetter('deviceid'))
|
||||
for j in range(len(vm_volumes)):
|
||||
volume = vm_volumes[j]
|
||||
disk = template_disks[j]
|
||||
self.assertEqual(
|
||||
volume.size,
|
||||
disk["virtualSize"],
|
||||
msg="VM Volume(diskNumber: {}) network mismatch, expected = {}, result = {}".format(disk["diskNumber"], disk["virtualSize"], volume.size)
|
||||
)
|
||||
|
||||
@attr(tags=["advanced", "advancedns", "smoke", "sg", "dev"], required_hardware="false")
|
||||
@skipTestIf("hypervisorNotSupported")
|
||||
def test_01_vapps_vm_cycle(self):
|
||||
|
|
@ -1939,8 +1914,6 @@ class TestVAppsVM(cloudstackTestCase):
|
|||
|
||||
# Verify nics
|
||||
self.verify_nics(nicnetworklist, vm.id)
|
||||
# Verify disks
|
||||
self.verify_disks(disks, vm.id)
|
||||
# Verify properties
|
||||
original_properties = vm_service['properties']
|
||||
vm_properties = get_vm_vapp_configs(self.apiclient, self.config, self.zone, vm.instancename)
|
||||
|
|
|
|||
|
|
@ -429,21 +429,21 @@ def get_test_ovf_templates(apiclient, zone_id=None, test_ovf_templates=None, hyp
|
|||
template = Template.register(apiclient, test_template, zoneid=zone_id, hypervisor=hypervisor.lower(), randomize_name=False)
|
||||
template.download(apiclient)
|
||||
retries = 3
|
||||
while (template.details == None or len(template.details.__dict__) == 0) and retries > 0:
|
||||
while (not hasattr(template, 'deployasisdetails') or len(template.deployasisdetails.__dict__) == 0) and retries > 0:
|
||||
time.sleep(10)
|
||||
template_list = Template.list(apiclient, id=template.id, zoneid=zone_id, templatefilter='all')
|
||||
if isinstance(template_list, list):
|
||||
template = Template(template_list[0].__dict__)
|
||||
retries = retries - 1
|
||||
if template.details == None or len(template.details.__dict__) == 0:
|
||||
if not hasattr(template, 'deployasisdetails') or len(template.deployasisdetails.__dict__) == 0:
|
||||
template.delete(apiclient)
|
||||
else:
|
||||
result.append(template)
|
||||
|
||||
if templates:
|
||||
for template in templates:
|
||||
if template.isready and template.ispublic and template.details != None and len(template.details.__dict__) > 0:
|
||||
result.append(template.__dict__)
|
||||
if template.isready and template.ispublic and template.deployasisdetails and len(template.deployasisdetails.__dict__) > 0:
|
||||
result.append(template)
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -42,7 +42,7 @@ export function api (command, args = {}, method = 'GET', data = {}) {
|
|||
export function login (arg) {
|
||||
const params = new URLSearchParams()
|
||||
params.append('command', 'login')
|
||||
params.append('username', arg.username)
|
||||
params.append('username', arg.username || arg.email)
|
||||
params.append('password', arg.password)
|
||||
params.append('domain', arg.domain)
|
||||
params.append('response', 'json')
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
slot="addonBefore"
|
||||
trigger="click"
|
||||
v-model="visibleFilter">
|
||||
<template slot="content">
|
||||
<template slot="content" v-if="visibleFilter">
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:form="form"
|
||||
|
|
@ -104,7 +104,7 @@
|
|||
class="filter-button"
|
||||
size="small"
|
||||
@click="() => { searchQuery = null }">
|
||||
<a-icon type="filter" :theme="Object.keys(searchParams).length > 0 ? 'twoTone' : 'outlined'" />
|
||||
<a-icon type="filter" :theme="isFiltered ? 'twoTone' : 'outlined'" />
|
||||
</a-button>
|
||||
</a-popover>
|
||||
</a-input-search>
|
||||
|
|
@ -140,7 +140,8 @@ export default {
|
|||
fields: [],
|
||||
inputKey: null,
|
||||
inputValue: null,
|
||||
fieldValues: {}
|
||||
fieldValues: {},
|
||||
isFiltered: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -157,6 +158,13 @@ export default {
|
|||
if (to && to.query && 'q' in to.query) {
|
||||
this.searchQuery = to.query.q
|
||||
}
|
||||
this.isFiltered = false
|
||||
this.searchFilters.some(item => {
|
||||
if (this.searchParams[item]) {
|
||||
this.isFiltered = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
|
@ -417,6 +425,7 @@ export default {
|
|||
field[item] = undefined
|
||||
this.form.setFieldsValue(field)
|
||||
})
|
||||
this.isFiltered = false
|
||||
this.inputKey = null
|
||||
this.inputValue = null
|
||||
this.searchQuery = null
|
||||
|
|
@ -430,6 +439,7 @@ export default {
|
|||
if (err) {
|
||||
return
|
||||
}
|
||||
this.isFiltered = true
|
||||
for (const key in values) {
|
||||
const input = values[key]
|
||||
if (input === '' || input === null || input === undefined) {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export default {
|
|||
label: 'label.edit',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true,
|
||||
args: ['name', 'displaytext']
|
||||
args: ['name', 'displaytext', 'tags']
|
||||
}, {
|
||||
api: 'updateDiskOffering',
|
||||
icon: 'lock',
|
||||
|
|
@ -216,7 +216,7 @@ export default {
|
|||
label: 'label.edit',
|
||||
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
|
||||
dataView: true,
|
||||
args: ['name', 'displaytext', 'availability'],
|
||||
args: ['name', 'displaytext', 'availability', 'tags'],
|
||||
mapping: {
|
||||
availability: {
|
||||
options: ['Optional', 'Required']
|
||||
|
|
|
|||
|
|
@ -284,7 +284,13 @@ export default {
|
|||
label: 'label.action.create.volume',
|
||||
dataView: true,
|
||||
show: (record) => { return record.state === 'BackedUp' },
|
||||
args: ['snapshotid', 'name'],
|
||||
args: (record, store) => {
|
||||
var fields = ['snapshotid', 'name']
|
||||
if (record.volumetype === 'ROOT') {
|
||||
fields.push('diskofferingid')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
mapping: {
|
||||
snapshotid: {
|
||||
value: (record) => { return record.id }
|
||||
|
|
|
|||
|
|
@ -983,13 +983,13 @@ export default {
|
|||
continue
|
||||
}
|
||||
if (!input === undefined || input === null ||
|
||||
(input === '' && !['updateStoragePool', 'updateHost', 'updatePhysicalNetwork'].includes(action.api))) {
|
||||
(input === '' && !['updateStoragePool', 'updateHost', 'updatePhysicalNetwork', 'updateDiskOffering', 'updateNetworkOffering'].includes(action.api))) {
|
||||
if (param.type === 'boolean') {
|
||||
params[key] = false
|
||||
}
|
||||
break
|
||||
}
|
||||
if (!input) {
|
||||
if (!input && !['tags'].includes(key)) {
|
||||
continue
|
||||
}
|
||||
if (action.mapping && key in action.mapping && action.mapping[key].options) {
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.tabType = this.$parent.tab === 'Ingress Rule' ? 'ingress' : 'egress'
|
||||
this.tabType = this.$parent.tab === this.$t('label.ingress.rule') ? 'ingress' : 'egress'
|
||||
this.rules = this.tabType === 'ingress' ? this.resource.ingressrule : this.resource.egressrule
|
||||
},
|
||||
handleAddRule () {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.cloud.hypervisor.vmware.mo;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
|
|
@ -28,6 +29,7 @@ import java.util.Arrays;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
|
@ -37,17 +39,6 @@ import javax.xml.transform.TransformerFactory;
|
|||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import com.vmware.vim25.ConcurrentAccessFaultMsg;
|
||||
import com.vmware.vim25.DuplicateNameFaultMsg;
|
||||
import com.vmware.vim25.FileFaultFaultMsg;
|
||||
import com.vmware.vim25.InsufficientResourcesFaultFaultMsg;
|
||||
import com.vmware.vim25.InvalidDatastoreFaultMsg;
|
||||
import com.vmware.vim25.InvalidNameFaultMsg;
|
||||
import com.vmware.vim25.InvalidStateFaultMsg;
|
||||
import com.vmware.vim25.OutOfBoundsFaultMsg;
|
||||
import com.vmware.vim25.RuntimeFaultFaultMsg;
|
||||
import com.vmware.vim25.TaskInProgressFaultMsg;
|
||||
import com.vmware.vim25.VmConfigFaultFaultMsg;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
|
@ -80,19 +71,20 @@ import com.cloud.utils.db.GlobalLock;
|
|||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.utils.nicira.nvp.plugin.NiciraNvpApiVersion;
|
||||
import com.vmware.vim25.OvfCreateDescriptorParams;
|
||||
import com.vmware.vim25.OvfCreateDescriptorResult;
|
||||
import com.vmware.vim25.AlreadyExistsFaultMsg;
|
||||
import com.vmware.vim25.BoolPolicy;
|
||||
import com.vmware.vim25.CustomFieldStringValue;
|
||||
import com.vmware.vim25.ClusterConfigInfoEx;
|
||||
import com.vmware.vim25.DatacenterConfigInfo;
|
||||
import com.vmware.vim25.ConcurrentAccessFaultMsg;
|
||||
import com.vmware.vim25.CustomFieldStringValue;
|
||||
import com.vmware.vim25.DVPortSetting;
|
||||
import com.vmware.vim25.DVPortgroupConfigInfo;
|
||||
import com.vmware.vim25.DVPortgroupConfigSpec;
|
||||
import com.vmware.vim25.DVSSecurityPolicy;
|
||||
import com.vmware.vim25.DVSTrafficShapingPolicy;
|
||||
import com.vmware.vim25.DatacenterConfigInfo;
|
||||
import com.vmware.vim25.DuplicateNameFaultMsg;
|
||||
import com.vmware.vim25.DynamicProperty;
|
||||
import com.vmware.vim25.FileFaultFaultMsg;
|
||||
import com.vmware.vim25.HostNetworkSecurityPolicy;
|
||||
import com.vmware.vim25.HostNetworkTrafficShapingPolicy;
|
||||
import com.vmware.vim25.HostPortGroup;
|
||||
|
|
@ -101,6 +93,10 @@ import com.vmware.vim25.HostVirtualSwitch;
|
|||
import com.vmware.vim25.HttpNfcLeaseDeviceUrl;
|
||||
import com.vmware.vim25.HttpNfcLeaseInfo;
|
||||
import com.vmware.vim25.HttpNfcLeaseState;
|
||||
import com.vmware.vim25.InsufficientResourcesFaultFaultMsg;
|
||||
import com.vmware.vim25.InvalidDatastoreFaultMsg;
|
||||
import com.vmware.vim25.InvalidNameFaultMsg;
|
||||
import com.vmware.vim25.InvalidStateFaultMsg;
|
||||
import com.vmware.vim25.LocalizedMethodFault;
|
||||
import com.vmware.vim25.LongPolicy;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
|
|
@ -108,11 +104,16 @@ import com.vmware.vim25.MethodFault;
|
|||
import com.vmware.vim25.NumericRange;
|
||||
import com.vmware.vim25.ObjectContent;
|
||||
import com.vmware.vim25.OptionValue;
|
||||
import com.vmware.vim25.OutOfBoundsFaultMsg;
|
||||
import com.vmware.vim25.OvfCreateDescriptorParams;
|
||||
import com.vmware.vim25.OvfCreateDescriptorResult;
|
||||
import com.vmware.vim25.OvfCreateImportSpecParams;
|
||||
import com.vmware.vim25.OvfCreateImportSpecResult;
|
||||
import com.vmware.vim25.OvfFileItem;
|
||||
import com.vmware.vim25.OvfFile;
|
||||
import com.vmware.vim25.OvfFileItem;
|
||||
import com.vmware.vim25.ParaVirtualSCSIController;
|
||||
import com.vmware.vim25.RuntimeFaultFaultMsg;
|
||||
import com.vmware.vim25.TaskInProgressFaultMsg;
|
||||
import com.vmware.vim25.VMwareDVSConfigSpec;
|
||||
import com.vmware.vim25.VMwareDVSPortSetting;
|
||||
import com.vmware.vim25.VMwareDVSPortgroupPolicy;
|
||||
|
|
@ -121,25 +122,24 @@ import com.vmware.vim25.VMwareDVSPvlanMapEntry;
|
|||
import com.vmware.vim25.VirtualBusLogicController;
|
||||
import com.vmware.vim25.VirtualController;
|
||||
import com.vmware.vim25.VirtualDevice;
|
||||
import com.vmware.vim25.VirtualDisk;
|
||||
import com.vmware.vim25.VirtualDeviceConfigSpec;
|
||||
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
|
||||
import com.vmware.vim25.VirtualDisk;
|
||||
import com.vmware.vim25.VirtualIDEController;
|
||||
import com.vmware.vim25.VirtualLsiLogicController;
|
||||
import com.vmware.vim25.VirtualLsiLogicSASController;
|
||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||
import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
|
||||
import com.vmware.vim25.VirtualMachineImportSpec;
|
||||
import com.vmware.vim25.VirtualMachineVideoCard;
|
||||
import com.vmware.vim25.VirtualSCSIController;
|
||||
import com.vmware.vim25.VirtualSCSISharing;
|
||||
import com.vmware.vim25.VirtualMachineImportSpec;
|
||||
import com.vmware.vim25.VmConfigFaultFaultMsg;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchTrunkVlanSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec;
|
||||
import java.io.FileWriter;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HypervisorHostHelper {
|
||||
private static final Logger s_logger = Logger.getLogger(HypervisorHostHelper.class);
|
||||
|
|
@ -153,6 +153,48 @@ public class HypervisorHostHelper {
|
|||
public static final String VSPHERE_DATASTORE_BASE_FOLDER = "fcd";
|
||||
public static final String VSPHERE_DATASTORE_HIDDEN_FOLDER = ".hidden";
|
||||
|
||||
protected final static Map<String, Integer> apiVersionHardwareVersionMap;
|
||||
|
||||
static {
|
||||
apiVersionHardwareVersionMap = new HashMap<String, Integer>();
|
||||
apiVersionHardwareVersionMap.put("3.5", 4);
|
||||
apiVersionHardwareVersionMap.put("3.6", 4);
|
||||
apiVersionHardwareVersionMap.put("3.7", 4);
|
||||
apiVersionHardwareVersionMap.put("3.8", 4);
|
||||
apiVersionHardwareVersionMap.put("3.9", 4);
|
||||
apiVersionHardwareVersionMap.put("4.0", 7);
|
||||
apiVersionHardwareVersionMap.put("4.1", 7);
|
||||
apiVersionHardwareVersionMap.put("4.2", 7);
|
||||
apiVersionHardwareVersionMap.put("4.3", 7);
|
||||
apiVersionHardwareVersionMap.put("4.4", 7);
|
||||
apiVersionHardwareVersionMap.put("4.5", 7);
|
||||
apiVersionHardwareVersionMap.put("4.6", 7);
|
||||
apiVersionHardwareVersionMap.put("4.7", 7);
|
||||
apiVersionHardwareVersionMap.put("4.8", 7);
|
||||
apiVersionHardwareVersionMap.put("4.9", 7);
|
||||
apiVersionHardwareVersionMap.put("5.0", 8);
|
||||
apiVersionHardwareVersionMap.put("5.1", 9);
|
||||
apiVersionHardwareVersionMap.put("5.2", 9);
|
||||
apiVersionHardwareVersionMap.put("5.3", 9);
|
||||
apiVersionHardwareVersionMap.put("5.4", 9);
|
||||
apiVersionHardwareVersionMap.put("5.5", 10);
|
||||
apiVersionHardwareVersionMap.put("5.6", 10);
|
||||
apiVersionHardwareVersionMap.put("5.7", 10);
|
||||
apiVersionHardwareVersionMap.put("5.8", 10);
|
||||
apiVersionHardwareVersionMap.put("5.9", 10);
|
||||
apiVersionHardwareVersionMap.put("6.0", 11);
|
||||
apiVersionHardwareVersionMap.put("6.1", 11);
|
||||
apiVersionHardwareVersionMap.put("6.2", 11);
|
||||
apiVersionHardwareVersionMap.put("6.3", 11);
|
||||
apiVersionHardwareVersionMap.put("6.4", 11);
|
||||
apiVersionHardwareVersionMap.put("6.5", 13);
|
||||
apiVersionHardwareVersionMap.put("6.6", 13);
|
||||
apiVersionHardwareVersionMap.put("6.7", 14);
|
||||
apiVersionHardwareVersionMap.put("6.8", 14);
|
||||
apiVersionHardwareVersionMap.put("6.9", 14);
|
||||
apiVersionHardwareVersionMap.put("7.0", 17);
|
||||
}
|
||||
|
||||
public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, ObjectContent[] ocs, String name, String instanceNameCustomField) {
|
||||
|
||||
if (ocs != null && ocs.length > 0) {
|
||||
|
|
@ -2211,4 +2253,36 @@ public class HypervisorHostHelper {
|
|||
dsMo.makeDirectory(hiddenFolderPath, hyperHost.getHyperHostDatacenter());
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer getHostHardwareVersion(VmwareHypervisorHost host) {
|
||||
Integer version = null;
|
||||
HostMO hostMo = new HostMO(host.getContext(), host.getMor());
|
||||
String hostApiVersion = "";
|
||||
try {
|
||||
hostApiVersion = hostMo.getHostAboutInfo().getApiVersion();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (hostApiVersion == null) {
|
||||
hostApiVersion = "";
|
||||
}
|
||||
version = apiVersionHardwareVersionMap.get(hostApiVersion);
|
||||
return version;
|
||||
}
|
||||
|
||||
/*
|
||||
Finds minimum host hardware version as String, of two hosts when both of them are not null
|
||||
and hardware version of both hosts is different.
|
||||
Return null otherwise
|
||||
*/
|
||||
public static String getMinimumHostHardwareVersion(VmwareHypervisorHost host1, VmwareHypervisorHost host2) {
|
||||
String hardwareVersion = null;
|
||||
if (host1 != null & host2 != null) {
|
||||
Integer host1Version = getHostHardwareVersion(host1);
|
||||
Integer host2Version = getHostHardwareVersion(host2);
|
||||
if (host1Version != null && host2Version != null && !host1Version.equals(host2Version)) {
|
||||
hardwareVersion = String.valueOf(Math.min(host1Version, host2Version));
|
||||
}
|
||||
}
|
||||
return hardwareVersion;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
// under the License.
|
||||
package com.cloud.hypervisor.vmware.mo;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
|
@ -35,10 +37,21 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.Future;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.vmware.vim25.InvalidStateFaultMsg;
|
||||
import com.vmware.vim25.RuntimeFaultFaultMsg;
|
||||
import com.vmware.vim25.VirtualMachineTicket;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.SnapshotDescriptor.SnapshotInfo;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareContext;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareHelper;
|
||||
import com.cloud.utils.ActionDelegate;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
import com.cloud.utils.script.Script;
|
||||
import com.google.gson.Gson;
|
||||
import com.vmware.vim25.ArrayOfManagedObjectReference;
|
||||
import com.vmware.vim25.ChoiceOption;
|
||||
|
|
@ -92,6 +105,7 @@ import com.vmware.vim25.VirtualMachineConfigInfo;
|
|||
import com.vmware.vim25.VirtualMachineConfigOption;
|
||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineConfigSummary;
|
||||
import com.vmware.vim25.VirtualMachineDefinedProfileSpec;
|
||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||
import com.vmware.vim25.VirtualMachineFileLayoutEx;
|
||||
import com.vmware.vim25.VirtualMachineMessage;
|
||||
|
|
@ -106,18 +120,6 @@ import com.vmware.vim25.VirtualMachineSnapshotInfo;
|
|||
import com.vmware.vim25.VirtualMachineSnapshotTree;
|
||||
import com.vmware.vim25.VirtualSCSIController;
|
||||
import com.vmware.vim25.VirtualSCSISharing;
|
||||
import com.vmware.vim25.VirtualMachineDefinedProfileSpec;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.SnapshotDescriptor.SnapshotInfo;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareContext;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareHelper;
|
||||
import com.cloud.utils.ActionDelegate;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
public class VirtualMachineMO extends BaseMO {
|
||||
private static final Logger s_logger = Logger.getLogger(VirtualMachineMO.class);
|
||||
|
|
@ -460,9 +462,13 @@ public class VirtualMachineMO extends BaseMO {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean changeDatastore(ManagedObjectReference morDataStore) throws Exception {
|
||||
public boolean changeDatastore(ManagedObjectReference morDataStore, VmwareHypervisorHost targetHost) throws Exception {
|
||||
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
|
||||
relocateSpec.setDatastore(morDataStore);
|
||||
if (targetHost != null) {
|
||||
relocateSpec.setHost(targetHost.getMor());
|
||||
relocateSpec.setPool(targetHost.getHyperHostOwnerResourcePool());
|
||||
}
|
||||
|
||||
ManagedObjectReference morTask = _context.getService().relocateVMTask(_mor, relocateSpec, null);
|
||||
|
||||
|
|
@ -3531,4 +3537,13 @@ public class VirtualMachineMO extends BaseMO {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire VNC ticket for console proxy.
|
||||
* Since VMware version 7
|
||||
*/
|
||||
public String acquireVncTicket() throws InvalidStateFaultMsg, RuntimeFaultFaultMsg {
|
||||
VirtualMachineTicket ticket = _context.getService().acquireTicket(_mor, "webmks");
|
||||
return ticket.getTicket();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,17 @@ import javax.xml.datatype.XMLGregorianCalendar;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DiskControllerType;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
import com.cloud.hypervisor.vmware.mo.LicenseAssignmentManagerMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.exception.ExceptionUtil;
|
||||
import com.vmware.vim25.DistributedVirtualSwitchPortConnection;
|
||||
import com.vmware.vim25.DynamicProperty;
|
||||
import com.vmware.vim25.GuestOsDescriptor;
|
||||
|
|
@ -56,7 +67,6 @@ import com.vmware.vim25.VirtualCdromRemotePassthroughBackingInfo;
|
|||
import com.vmware.vim25.VirtualDevice;
|
||||
import com.vmware.vim25.VirtualDeviceBackingInfo;
|
||||
import com.vmware.vim25.VirtualDeviceConnectInfo;
|
||||
import com.vmware.vim25.VirtualUSBController;
|
||||
import com.vmware.vim25.VirtualDisk;
|
||||
import com.vmware.vim25.VirtualDiskFlatVer1BackingInfo;
|
||||
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
|
||||
|
|
@ -72,21 +82,10 @@ import com.vmware.vim25.VirtualEthernetCardOpaqueNetworkBackingInfo;
|
|||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineSnapshotTree;
|
||||
import com.vmware.vim25.VirtualPCNet32;
|
||||
import com.vmware.vim25.VirtualUSBController;
|
||||
import com.vmware.vim25.VirtualVmxnet2;
|
||||
import com.vmware.vim25.VirtualVmxnet3;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.DiskControllerType;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
|
||||
import com.cloud.hypervisor.vmware.mo.LicenseAssignmentManagerMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.exception.ExceptionUtil;
|
||||
|
||||
public class VmwareHelper {
|
||||
@SuppressWarnings("unused")
|
||||
private static final Logger s_logger = Logger.getLogger(VmwareHelper.class);
|
||||
|
|
@ -744,4 +743,18 @@ public class VmwareHelper {
|
|||
return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
|
||||
}
|
||||
|
||||
public static HostMO getHostMOFromHostName(final VmwareContext context, final String hostName) {
|
||||
HostMO host = null;
|
||||
if (com.cloud.utils.StringUtils.isNotBlank(hostName) && hostName.contains("@")) {
|
||||
String hostMorInfo = hostName.split("@")[0];
|
||||
if (hostMorInfo.contains(":")) {
|
||||
ManagedObjectReference morHost = new ManagedObjectReference();
|
||||
morHost.setType(hostMorInfo.split(":")[0]);
|
||||
morHost.setValue(hostMorInfo.split(":")[1]);
|
||||
host = new HostMO(context, morHost);
|
||||
}
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue