Merge branch '4.19' into 4.20.merge

This commit is contained in:
Daan Hoogland 2024-12-03 16:32:15 +01:00
commit da54234585
51 changed files with 1776 additions and 297 deletions

View File

@ -19,6 +19,9 @@ package com.cloud.agent.api.to;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.PortForwardingRule;
import com.cloud.utils.net.NetUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* PortForwardingRuleTO specifies one port forwarding rule.
@ -29,6 +32,8 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
String dstIp;
int[] dstPortRange;
List<String> sourceCidrList;
protected PortForwardingRuleTO() {
super();
}
@ -37,6 +42,7 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
super(rule, srcVlanTag, srcIp);
this.dstIp = rule.getDestinationIpAddress().addr();
this.dstPortRange = new int[] {rule.getDestinationPortStart(), rule.getDestinationPortEnd()};
this.sourceCidrList = rule.getSourceCidrList();
}
public PortForwardingRuleTO(long id, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol,
@ -58,4 +64,11 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
return NetUtils.portRangeToString(dstPortRange);
}
public String getSourceCidrListAsString() {
if (sourceCidrList != null) {
return StringUtils.join(sourceCidrList, ",");
}
return null;
}
}

View File

@ -26,6 +26,7 @@ import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.utils.net.Ip;
import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd;
public interface RulesService {
Pair<List<? extends FirewallRule>, Integer> searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId,
@ -81,6 +82,8 @@ public interface RulesService {
boolean disableStaticNat(long ipId) throws ResourceUnavailableException, NetworkRuleConflictException, InsufficientAddressCapacityException;
PortForwardingRule updatePortForwardingRule(long id, Integer privatePort, Integer privateEndPort, Long virtualMachineId, Ip vmGuestIp, String customId, Boolean forDisplay);
PortForwardingRule updatePortForwardingRule(UpdatePortForwardingRuleCmd cmd);
void validatePortForwardingSourceCidrList(List<String> sourceCidrList);
}

View File

@ -244,6 +244,7 @@ public class ApiConstants {
public static final String ICMP_TYPE = "icmptype";
public static final String ID = "id";
public static final String IDS = "ids";
public static final String IMPORT_INSTANCE_HOST_ID = "importinstancehostid";
public static final String INDEX = "index";
public static final String INSTANCES_DISKS_STATS_RETENTION_ENABLED = "instancesdisksstatsretentionenabled";
public static final String INSTANCES_DISKS_STATS_RETENTION_TIME = "instancesdisksstatsretentiontime";
@ -464,6 +465,7 @@ public class ApiConstants {
public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
public static final String SNAPSHOT_TYPE = "snapshottype";
public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
public static final String SOURCE_CIDR_LIST = "sourcecidrlist";
public static final String SOURCE_ZONE_ID = "sourcezoneid";
public static final String SSL_VERIFICATION = "sslverification";
public static final String START_ASN = "startasn";

View File

@ -144,15 +144,19 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
private String clusterName;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
description = "(only for importing VMs from VMware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
description = "(only for importing VMs from VMware to KVM) optional - the host to perform the virt-v2v conversion from VMware to KVM.")
private Long convertInstanceHostId;
@Parameter(name = ApiConstants.IMPORT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, since = "4.19.2",
description = "(only for importing VMs from VMware to KVM) optional - the host to import the converted instance from VMware to KVM.")
private Long importInstanceHostId;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class,
description = "(only for importing VMs from VMware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
private Long convertStoragePoolId;
@Parameter(name = ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, type = CommandType.BOOLEAN,
description = "(only for importing VMs from VMware to KVM) optional - if true, forces MS to import VM file(s) to temporary storage, else uses KVM Host if ovftool is available, falls back to MS if not.")
description = "(only for importing VMs from VMware to KVM) optional - if true, forces MS to export OVF from VMware to temporary storage, else uses KVM Host if ovftool is available, falls back to MS if not.")
private Boolean forceMsToImportVmFiles;
/////////////////////////////////////////////////////
@ -199,6 +203,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
return convertInstanceHostId;
}
public Long getImportInstanceHostId() {
return importInstanceHostId;
}
public Long getConvertStoragePoolId() {
return convertStoragePoolId;
}

View File

@ -105,8 +105,12 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
description = "the ID of the virtual machine for the port forwarding rule")
private Long virtualMachineId;
@Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.")
private List<String> cidrlist;
@Parameter(name = ApiConstants.CIDR_LIST,
type = CommandType.LIST,
collectionType = CommandType.STRING,
description = " the source CIDR list to allow traffic from; all other CIDRs will be blocked. " +
"Multiple entries must be separated by a single comma character (,). This param will be used only for VPC tiers. By default, all CIDRs are allowed.")
private List<String> sourceCidrList;
@Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "if true, firewall rule for source/end public port is automatically created; "
+ "if false - firewall rule has to be created explicitly. If not specified 1) defaulted to false when PF"
@ -155,11 +159,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
@Override
public List<String> getSourceCidrList() {
if (cidrlist != null) {
throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall "
+ "rule for the specific cidr, please refer to createFirewallRule command");
}
return null;
return sourceCidrList;
}
public Boolean getOpenFirewall() {
@ -332,12 +332,6 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
@Override
public void create() {
// cidr list parameter is deprecated
if (cidrlist != null) {
throw new InvalidParameterValueException(
"Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command");
}
Ip privateIp = getVmSecondaryIp();
if (privateIp != null) {
if (!NetUtils.isValidIp4(privateIp.toString())) {
@ -345,6 +339,8 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
}
}
_rulesService.validatePortForwardingSourceCidrList(sourceCidrList);
try {
PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall(), isDisplay());
setEntityId(result.getId());

View File

@ -32,6 +32,8 @@ import com.cloud.network.rules.PortForwardingRule;
import com.cloud.user.Account;
import com.cloud.utils.net.Ip;
import java.util.List;
@APICommand(name = "updatePortForwardingRule",
responseObject = FirewallRuleResponse.class,
description = "Updates a port forwarding rule. Only the private port and the virtual machine can be updated.", entityType = {PortForwardingRule.class},
@ -63,6 +65,13 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd {
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin})
private Boolean display;
@Parameter(name = ApiConstants.CIDR_LIST,
type = CommandType.LIST,
collectionType = CommandType.STRING,
description = " the source CIDR list to allow traffic from; all other CIDRs will be blocked. " +
"Multiple entries must be separated by a single comma character (,). This param will be used only for VPC tiers. By default, all CIDRs are allowed.")
private List<String> sourceCidrList;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -94,6 +103,10 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd {
return display;
}
public List<String> getSourceCidrList() {
return sourceCidrList;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -130,7 +143,7 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd {
@Override
public void execute() {
PortForwardingRule rule = _rulesService.updatePortForwardingRule(getId(), getPrivatePort(), getPrivateEndPort(), getVirtualMachineId(), getVmGuestIp(), getCustomId(), getDisplay());
PortForwardingRule rule = _rulesService.updatePortForwardingRule(this);
FirewallRuleResponse fwResponse = new FirewallRuleResponse();
if (rule != null) {
fwResponse = _responseGenerator.createPortForwardingRuleResponse(rule);

View File

@ -104,7 +104,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the load balancer")
private Long domainId;
@Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, since = "4.18.0.0", description = "the CIDR list to allow traffic, "
@Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, since = "4.18.0.0", description = "the source CIDR list to allow traffic from; "
+ "all other CIDRs will be blocked. Multiple entries must be separated by a single comma character (,). By default, all CIDRs are allowed.")
private List<String> cidrlist;

View File

@ -20,6 +20,8 @@ import org.apache.cloudstack.vm.UnmanagedInstanceTO;
public class ConvertInstanceAnswer extends Answer {
private String temporaryConvertUuid;
public ConvertInstanceAnswer() {
super();
}
@ -34,6 +36,15 @@ public class ConvertInstanceAnswer extends Answer {
this.convertedInstance = convertedInstance;
}
public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) {
super(command, true, "");
this.temporaryConvertUuid = temporaryConvertUuid;
}
public String getTemporaryConvertUuid() {
return temporaryConvertUuid;
}
public UnmanagedInstanceTO getConvertedInstance() {
return convertedInstance;
}

View File

@ -0,0 +1,40 @@
// 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;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
public class ImportConvertedInstanceAnswer extends Answer {
public ImportConvertedInstanceAnswer() {
super();
}
private UnmanagedInstanceTO convertedInstance;
public ImportConvertedInstanceAnswer(Command command, boolean success, String details) {
super(command, success, details);
}
public ImportConvertedInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) {
super(command, true, "");
this.convertedInstance = convertedInstance;
}
public UnmanagedInstanceTO getConvertedInstance() {
return convertedInstance;
}
}

View File

@ -0,0 +1,63 @@
// 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;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.RemoteInstanceTO;
import java.util.List;
public class ImportConvertedInstanceCommand extends Command {
private RemoteInstanceTO sourceInstance;
private List<String> destinationStoragePools;
private DataStoreTO conversionTemporaryLocation;
private String temporaryConvertUuid;
public ImportConvertedInstanceCommand() {
}
public ImportConvertedInstanceCommand(RemoteInstanceTO sourceInstance,
List<String> destinationStoragePools,
DataStoreTO conversionTemporaryLocation, String temporaryConvertUuid) {
this.sourceInstance = sourceInstance;
this.destinationStoragePools = destinationStoragePools;
this.conversionTemporaryLocation = conversionTemporaryLocation;
this.temporaryConvertUuid = temporaryConvertUuid;
}
public RemoteInstanceTO getSourceInstance() {
return sourceInstance;
}
public List<String> getDestinationStoragePools() {
return destinationStoragePools;
}
public DataStoreTO getConversionTemporaryLocation() {
return conversionTemporaryLocation;
}
public String getTemporaryConvertUuid() {
return temporaryConvertUuid;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -41,7 +41,7 @@ public class SetPortForwardingRulesConfigItem extends AbstractConfigItemFacade {
for (final PortForwardingRuleTO rule : command.getRules()) {
final ForwardingRule fwdRule = new ForwardingRule(rule.revoked(), rule.getProtocol().toLowerCase(), rule.getSrcIp(), rule.getStringSrcPortRange(), rule.getDstIp(),
rule.getStringDstPortRange());
rule.getStringDstPortRange(), rule.getSourceCidrListAsString());
rules.add(fwdRule);
}

View File

@ -26,18 +26,21 @@ public class ForwardingRule {
private String sourcePortRange;
private String destinationIpAddress;
private String destinationPortRange;
private String sourceCidrList;
public ForwardingRule() {
// Empty constructor for (de)serialization
}
public ForwardingRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress, String destinationPortRange) {
public ForwardingRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress, String destinationPortRange,
String sourceCidrList) {
this.revoke = revoke;
this.protocol = protocol;
this.sourceIpAddress = sourceIpAddress;
this.sourcePortRange = sourcePortRange;
this.destinationIpAddress = destinationIpAddress;
this.destinationPortRange = destinationPortRange;
this.sourceCidrList = sourceCidrList;
}
public boolean isRevoke() {
@ -88,4 +91,8 @@ public class ForwardingRule {
this.destinationPortRange = destinationPortRange;
}
public String getSourceCidrList() {
return sourceCidrList;
}
}

View File

@ -29,4 +29,6 @@ public interface FirewallRulesCidrsDao extends GenericDao<FirewallRulesCidrsVO,
@DB
List<FirewallRulesCidrsVO> listByFirewallRuleId(long firewallRuleId);
void updateSourceCidrsForRule(Long firewallRuleId, List<String> sourceCidrList);
}

View File

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import com.cloud.utils.db.DB;
@ -45,7 +46,7 @@ public class FirewallRulesCidrsDaoImpl extends GenericDaoBase<FirewallRulesCidrs
sc.setParameters("firewallRuleId", firewallRuleId);
List<FirewallRulesCidrsVO> results = search(sc, null);
List<String> cidrs = new ArrayList<String>(results.size());
List<String> cidrs = new ArrayList<>(results.size());
for (FirewallRulesCidrsVO result : results) {
cidrs.add(result.getCidr());
}
@ -63,9 +64,27 @@ public class FirewallRulesCidrsDaoImpl extends GenericDaoBase<FirewallRulesCidrs
return results;
}
@Override
public void updateSourceCidrsForRule(Long firewallRuleId, List<String> sourceCidrList) {
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
SearchCriteria<FirewallRulesCidrsVO> sc = CidrsSearch.create();
sc.setParameters("firewallRuleId", firewallRuleId);
remove(sc);
persist(firewallRuleId, sourceCidrList);
txn.commit();
}
@Override
@DB
public void persist(long firewallRuleId, List<String> sourceCidrs) {
if (CollectionUtils.isEmpty(sourceCidrs)) {
return;
}
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();

View File

@ -251,9 +251,6 @@ public class FirewallRulesDaoImpl extends GenericDaoBase<FirewallRuleVO, Long> i
}
public void saveSourceCidrs(FirewallRuleVO firewallRule, List<String> cidrList) {
if (cidrList == null) {
return;
}
_firewallRulesCidrsDao.persist(firewallRule.getId(), cidrList);
}

View File

@ -25,6 +25,7 @@ import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.cloud.utils.net.Ip;
@ -47,21 +48,30 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi
@Column(name = "instance_id")
private long virtualMachineId;
@Transient
List<String> sourceCidrs;
public PortForwardingRuleVO() {
}
public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId,
long accountId, long domainId, long instanceId) {
super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, null, null, null, null, null);
long accountId, long domainId, long instanceId, List<String> sourceCidrs) {
super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, sourceCidrs, null, null, null, null);
this.destinationIpAddress = dstIp;
this.virtualMachineId = instanceId;
this.destinationPortStart = dstPortStart;
this.destinationPortEnd = dstPortEnd;
this.sourceCidrs = sourceCidrs;
}
public PortForwardingRuleVO(String xId, long srcIpId, int srcPort, Ip dstIp, int dstPort, String protocol, List<String> sourceCidrs, long networkId, long accountId,
long domainId, long instanceId) {
this(xId, srcIpId, srcPort, srcPort, dstIp, dstPort, dstPort, protocol.toLowerCase(), networkId, accountId, domainId, instanceId);
public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId,
long accountId, long domainId, long instanceId) {
this(xId, srcIpId, srcPortStart, srcPortEnd, dstIp, dstPortStart, dstPortEnd, protocol.toLowerCase(), networkId, accountId, domainId, instanceId, null);
}
public PortForwardingRuleVO(String xId, long srcIpId, int srcPort, Ip dstIp, int dstPort, String protocol, long networkId, long accountId,
long domainId, long instanceId) {
this(xId, srcIpId, srcPort, srcPort, dstIp, dstPort, dstPort, protocol.toLowerCase(), networkId, accountId, domainId, instanceId, null);
}
@Override
@ -106,4 +116,13 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi
return null;
}
public void setSourceCidrList(List<String> sourceCidrs) {
this.sourceCidrs = sourceCidrs;
}
@Override
public List<String> getSourceCidrList() {
return sourceCidrs;
}
}

View File

@ -31,6 +31,9 @@ import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionLegacy;
@Component
public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRuleVO, Long> implements PortForwardingRulesDao {
@ -42,7 +45,7 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRul
protected final SearchBuilder<PortForwardingRuleVO> ActiveRulesSearchByAccount;
@Inject
protected FirewallRulesCidrsDao _portForwardingRulesCidrsDao;
protected FirewallRulesCidrsDao portForwardingRulesCidrsDao;
protected PortForwardingRulesDaoImpl() {
super();
@ -183,4 +186,43 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRul
sc.setParameters("vmIds", vmIds.toArray());
return batchExpunge(sc, batchSize);
}
public PortForwardingRuleVO persist(PortForwardingRuleVO portForwardingRule) {
return Transaction.execute((TransactionCallback<PortForwardingRuleVO>) transactionStatus -> {
PortForwardingRuleVO dbPfRule = super.persist(portForwardingRule);
portForwardingRulesCidrsDao.persist(portForwardingRule.getId(), portForwardingRule.getSourceCidrList());
List<String> cidrList = portForwardingRulesCidrsDao.getSourceCidrs(portForwardingRule.getId());
portForwardingRule.setSourceCidrList(cidrList);
return dbPfRule;
});
}
@Override
public boolean update(Long id, PortForwardingRuleVO entity) {
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
boolean success = super.update(id, entity);
if (!success) {
return false;
}
portForwardingRulesCidrsDao.updateSourceCidrsForRule(entity.getId(), entity.getSourceCidrList());
txn.commit();
return true;
}
@Override
public PortForwardingRuleVO findById(Long id) {
PortForwardingRuleVO rule = super.findById(id);
List<String> sourceCidrList = portForwardingRulesCidrsDao.getSourceCidrs(id);
rule.setSourceCidrList(sourceCidrList);
return rule;
}
}

View File

@ -50,6 +50,9 @@ public class VolumeVO implements Volume {
@Column(name = "id")
long id;
@Column(name = "last_id")
private long lastId;
@Column(name = "name")
String name;
@ -690,4 +693,12 @@ public class VolumeVO implements Volume {
public void setDeleteProtection(boolean deleteProtection) {
this.deleteProtection = deleteProtection;
}
public long getLastId() {
return lastId;
}
public void setLastId(long lastId) {
this.lastId = lastId;
}
}

View File

@ -70,3 +70,6 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data', 'removed', 'datetime COM
UPDATE `cloud`.`configuration` SET
`options` = 'FirstFitRouting,RandomAllocator,TestingAllocator,FirstFitAllocator,RecreateHostAllocator'
WHERE `name` = 'host.allocators.order';
-- Add last_id to the volumes table
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('volumes','last_id', 'bigint(20) unsigned DEFAULT NULL');

View File

@ -2293,6 +2293,8 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
if (success) {
VolumeVO volumeVO = _volumeDao.findById(destVolumeInfo.getId());
volumeVO.setFormat(ImageFormat.QCOW2);
volumeVO.setLastId(srcVolumeInfo.getId());
_volumeDao.update(volumeVO.getId(), volumeVO);
_volumeService.copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(Event.OperationSuccessed, null, srcVolumeInfo, destVolumeInfo, false);

View File

@ -1720,6 +1720,7 @@ public class VolumeServiceImpl implements VolumeService {
newVol.setPassphraseId(volume.getPassphraseId());
newVol.setEncryptFormat(volume.getEncryptFormat());
}
newVol.setLastId(volume.getId());
return volDao.persist(newVol);
}

View File

@ -3000,6 +3000,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return dataPath;
}
public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) {
return physicalDisk != null &&
physicalDisk.getPool().getType() == StoragePoolType.Linstor &&
physicalDisk.getFormat() != null &&
physicalDisk.getFormat()== PhysicalDiskFormat.RAW;
}
public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) {
return useBLOCKDiskType(physicalDisk) ? DiskDef.DiskType.BLOCK : DiskDef.DiskType.FILE;
}
public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException {
final Map<String, String> details = vmSpec.getDetails();
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
@ -3045,7 +3056,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data);
} else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool)) {
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Linstor)) {
physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data);
}
}
@ -3095,8 +3107,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final DiskDef disk = new DiskDef();
int devId = volume.getDiskSeq().intValue();
if (volume.getType() == Volume.Type.ISO) {
disk.defISODisk(volPath, devId, isUefiEnabled);
final DiskDef.DiskType diskType = getDiskType(physicalDisk);
disk.defISODisk(volPath, devId, isUefiEnabled, diskType);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
disk.setBusType(DiskDef.DiskBus.SCSI);
@ -3195,7 +3207,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (vmSpec.getType() != VirtualMachine.Type.User) {
final DiskDef iso = new DiskDef();
iso.defISODisk(sysvmISOPath);
iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
iso.setBusType(DiskDef.DiskBus.SCSI);
}
@ -3408,7 +3420,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
List<DiskDef> disks = getDisks(conn, vmName);
DiskDef configdrive = null;
for (DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && disk.getDiskLabel() == CONFIG_DRIVE_ISO_DISK_LABEL) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && CONFIG_DRIVE_ISO_DISK_LABEL.equals(disk.getDiskLabel())) {
configdrive = disk;
}
}
@ -3438,11 +3450,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final String name = isoPath.substring(index + 1);
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
final DiskDef.DiskType diskType = getDiskType(isoVol);
isoPath = isoVol.getPath();
iso.defISODisk(isoPath, diskSeq);
iso.defISODisk(isoPath, diskSeq, diskType);
} else {
iso.defISODisk(null, diskSeq);
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
}
final String result = attachOrDetachDevice(conn, true, vmName, iso.toString());
@ -3450,7 +3463,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final List<DiskDef> disks = getDisks(conn, vmName);
for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) {
&& (diskSeq == null || disk.getDiskLabel().equals(iso.getDiskLabel()))) {
cleanupDisk(disk);
}
}
@ -4046,7 +4059,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return stopVMInternal(conn, vmName, true);
}
String ret = stopVMInternal(conn, vmName, false);
if (ret == Script.ERR_TIMEOUT) {
if (Script.ERR_TIMEOUT.equals(ret)) {
ret = stopVMInternal(conn, vmName, true);
} else if (ret != null) {
/*

View File

@ -127,11 +127,10 @@ public class LibvirtDomainXMLParser {
}
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
} else if (device.equalsIgnoreCase("cdrom")) {
def.defISODisk(diskFile, i+1, diskLabel);
def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.FILE);
}
} else if (type.equalsIgnoreCase("block")) {
def.defBlockBasedDisk(diskDev, diskLabel,
DiskDef.DiskBus.valueOf(bus.toUpperCase()));
parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
}
if (StringUtils.isNotBlank(diskCacheMode)) {
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@ -450,6 +449,25 @@ public class LibvirtDomainXMLParser {
return node.getAttribute(attr);
}
/**
* Parse the disk block part of the libvirt XML.
* @param def
* @param device
* @param diskDev
* @param diskLabel
* @param bus
* @param diskFile
* @param curDiskIndex
*/
private void parseDiskBlock(DiskDef def, String device, String diskDev, String diskLabel, String bus,
String diskFile, int curDiskIndex) {
if (device.equalsIgnoreCase("disk")) {
def.defBlockBasedDisk(diskDev, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()));
} else if (device.equalsIgnoreCase("cdrom")) {
def.defISODisk(diskFile, curDiskIndex+1, diskLabel, DiskDef.DiskType.BLOCK);
}
}
public Integer getVncPort() {
return vncPort;
}

View File

@ -894,8 +894,8 @@ public class LibvirtVMDef {
}
}
public void defISODisk(String volPath) {
_diskType = DiskType.FILE;
public void defISODisk(String volPath, DiskType diskType) {
_diskType = diskType;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
_diskLabel = getDevLabel(3, DiskBus.IDE, true);
@ -904,8 +904,8 @@ public class LibvirtVMDef {
_bus = DiskBus.IDE;
}
public void defISODisk(String volPath, boolean isUefiEnabled) {
_diskType = DiskType.FILE;
public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) {
_diskType = diskType;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
_bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
@ -914,18 +914,18 @@ public class LibvirtVMDef {
_diskCacheMode = DiskCacheMode.NONE;
}
public void defISODisk(String volPath, Integer devId) {
defISODisk(volPath, devId, null);
public void defISODisk(String volPath, Integer devId, DiskType diskType) {
defISODisk(volPath, devId, null, diskType);
}
public void defISODisk(String volPath, Integer devId, String diskLabel) {
public void defISODisk(String volPath, Integer devId, String diskLabel, DiskType diskType) {
if (devId == null && StringUtils.isBlank(diskLabel)) {
LOGGER.debug(String.format("No ID or label informed for volume [%s].", volPath));
defISODisk(volPath);
defISODisk(volPath, diskType);
return;
}
_diskType = DiskType.FILE;
_diskType = diskType;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
@ -942,11 +942,11 @@ public class LibvirtVMDef {
_bus = DiskBus.IDE;
}
public void defISODisk(String volPath, Integer devId,boolean isSecure) {
public void defISODisk(String volPath, Integer devId, boolean isSecure, DiskType diskType) {
if (!isSecure) {
defISODisk(volPath, devId);
defISODisk(volPath, devId, diskType);
} else {
_diskType = DiskType.FILE;
_diskType = diskType;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
_diskLabel = getDevLabel(devId, DiskBus.SATA, true);

View File

@ -130,34 +130,25 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
final String temporaryConvertUuid = UUID.randomUUID().toString();
boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
boolean cleanupSecondaryStorage = false;
try {
boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
timeout, verboseModeEnabled);
if (!result) {
String err = String.format("The virt-v2v conversion for the OVF %s failed. " +
"Please check the agent logs for the virt-v2v output", ovfTemplateDirOnConversionLocation);
String err = String.format(
"The virt-v2v conversion for the OVF %s failed. Please check the agent logs " +
"for the virt-v2v output. Please try on a different kvm host which " +
"has a different virt-v2v version.",
ovfTemplateDirOnConversionLocation);
logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err);
}
String convertedBasePath = String.format("%s/%s", temporaryConvertPath, temporaryConvertUuid);
LibvirtDomainXMLParser xmlParser = parseMigratedVMXmlDomain(convertedBasePath);
List<KVMPhysicalDisk> temporaryDisks = xmlParser == null ?
getTemporaryDisksWithPrefixFromTemporaryPool(temporaryStoragePool, temporaryConvertPath, temporaryConvertUuid) :
getTemporaryDisksFromParsedXml(temporaryStoragePool, xmlParser, convertedBasePath);
List<KVMPhysicalDisk> destinationDisks = moveTemporaryDisksToDestination(temporaryDisks,
destinationStoragePools, storagePoolMgr);
cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks, temporaryStoragePool, temporaryConvertUuid);
UnmanagedInstanceTO convertedInstanceTO = getConvertedUnmanagedInstance(temporaryConvertUuid,
destinationDisks, xmlParser);
return new ConvertInstanceAnswer(cmd, convertedInstanceTO);
return new ConvertInstanceAnswer(cmd, temporaryConvertUuid);
} catch (Exception e) {
String error = String.format("Error converting instance %s from %s, due to: %s",
sourceInstanceName, sourceHypervisorType, e.getMessage());
logger.error(error, e);
cleanupSecondaryStorage = true;
return new ConvertInstanceAnswer(cmd, false, error);
} finally {
if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
@ -165,7 +156,7 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir);
FileUtil.deletePath(sourceOVFDir);
}
if (conversionTemporaryLocation instanceof NfsTO) {
if (cleanupSecondaryStorage && conversionTemporaryLocation instanceof NfsTO) {
logger.debug("Cleaning up secondary storage temporary location");
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
}

View File

@ -0,0 +1,299 @@
//
// 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.hypervisor.kvm.resource.wrapper;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ImportConvertedInstanceAnswer;
import com.cloud.agent.api.ImportConvertedInstanceCommand;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.RemoteInstanceTO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage;
import com.cloud.utils.FileUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
@ResourceWrapper(handles = ImportConvertedInstanceCommand.class)
public class LibvirtImportConvertedInstanceCommandWrapper extends CommandWrapper<ImportConvertedInstanceCommand, Answer, LibvirtComputingResource> {
@Override
public Answer execute(ImportConvertedInstanceCommand cmd, LibvirtComputingResource serverResource) {
RemoteInstanceTO sourceInstance = cmd.getSourceInstance();
Hypervisor.HypervisorType sourceHypervisorType = sourceInstance.getHypervisorType();
String sourceInstanceName = sourceInstance.getInstanceName();
List<String> destinationStoragePools = cmd.getDestinationStoragePools();
DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
final String temporaryConvertUuid = cmd.getTemporaryConvertUuid();
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr);
final String temporaryConvertPath = temporaryStoragePool.getLocalPath();
try {
String convertedBasePath = String.format("%s/%s", temporaryConvertPath, temporaryConvertUuid);
LibvirtDomainXMLParser xmlParser = parseMigratedVMXmlDomain(convertedBasePath);
List<KVMPhysicalDisk> temporaryDisks = xmlParser == null ?
getTemporaryDisksWithPrefixFromTemporaryPool(temporaryStoragePool, temporaryConvertPath, temporaryConvertUuid) :
getTemporaryDisksFromParsedXml(temporaryStoragePool, xmlParser, convertedBasePath);
List<KVMPhysicalDisk> destinationDisks = moveTemporaryDisksToDestination(temporaryDisks,
destinationStoragePools, storagePoolMgr);
cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks, temporaryStoragePool, temporaryConvertUuid);
UnmanagedInstanceTO convertedInstanceTO = getConvertedUnmanagedInstance(temporaryConvertUuid,
destinationDisks, xmlParser);
return new ImportConvertedInstanceAnswer(cmd, convertedInstanceTO);
} catch (Exception e) {
String error = String.format("Error converting instance %s from %s, due to: %s",
sourceInstanceName, sourceHypervisorType, e.getMessage());
logger.error(error, e);
return new ImportConvertedInstanceAnswer(cmd, false, error);
} finally {
if (conversionTemporaryLocation instanceof NfsTO) {
logger.debug("Cleaning up secondary storage temporary location");
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
}
}
}
protected KVMStoragePool getTemporaryStoragePool(DataStoreTO conversionTemporaryLocation, KVMStoragePoolManager storagePoolMgr) {
if (conversionTemporaryLocation instanceof NfsTO) {
NfsTO nfsTO = (NfsTO) conversionTemporaryLocation;
return storagePoolMgr.getStoragePoolByURI(nfsTO.getUrl());
} else {
PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) conversionTemporaryLocation;
return storagePoolMgr.getStoragePool(primaryDataStoreTO.getPoolType(), primaryDataStoreTO.getUuid());
}
}
protected List<KVMPhysicalDisk> getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser xmlParser, String convertedBasePath) {
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() == LibvirtVMDef.DiskDef.DiskType.FILE &&
x.getDeviceType() == LibvirtVMDef.DiskDef.DeviceType.DISK).collect(Collectors.toList());
if (CollectionUtils.isEmpty(disksDefs)) {
String err = String.format("Cannot find any disk defined on the converted XML domain %s.xml", convertedBasePath);
logger.error(err);
throw new CloudRuntimeException(err);
}
sanitizeDisksPath(disksDefs);
return getPhysicalDisksFromDefPaths(disksDefs, pool);
}
private List<KVMPhysicalDisk> getPhysicalDisksFromDefPaths(List<LibvirtVMDef.DiskDef> disksDefs, KVMStoragePool pool) {
List<KVMPhysicalDisk> disks = new ArrayList<>();
for (LibvirtVMDef.DiskDef diskDef : disksDefs) {
KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(diskDef.getDiskPath());
disks.add(physicalDisk);
}
return disks;
}
protected List<KVMPhysicalDisk> getTemporaryDisksWithPrefixFromTemporaryPool(KVMStoragePool pool, String path, String prefix) {
String msg = String.format("Could not parse correctly the converted XML domain, checking for disks on %s with prefix %s", path, prefix);
logger.info(msg);
pool.refresh();
List<KVMPhysicalDisk> disksWithPrefix = pool.listPhysicalDisks()
.stream()
.filter(x -> x.getName().startsWith(prefix) && !x.getName().endsWith(".xml"))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(disksWithPrefix)) {
msg = String.format("Could not find any converted disk with prefix %s on temporary location %s", prefix, path);
logger.error(msg);
throw new CloudRuntimeException(msg);
}
return disksWithPrefix;
}
private void cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
KVMStoragePool temporaryStoragePool,
String temporaryConvertUuid) {
for (KVMPhysicalDisk disk : disks) {
logger.info(String.format("Cleaning up temporary disk %s after conversion from temporary location", disk.getName()));
temporaryStoragePool.deletePhysicalDisk(disk.getName(), Storage.ImageFormat.QCOW2);
}
logger.info(String.format("Cleaning up temporary domain %s after conversion from temporary location", temporaryConvertUuid));
FileUtil.deleteFiles(temporaryStoragePool.getLocalPath(), temporaryConvertUuid, ".xml");
}
protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
for (LibvirtVMDef.DiskDef disk : disks) {
String[] diskPathParts = disk.getDiskPath().split("/");
String relativePath = diskPathParts[diskPathParts.length - 1];
disk.setDiskPath(relativePath);
}
}
protected List<KVMPhysicalDisk> moveTemporaryDisksToDestination(List<KVMPhysicalDisk> temporaryDisks,
List<String> destinationStoragePools,
KVMStoragePoolManager storagePoolMgr) {
List<KVMPhysicalDisk> targetDisks = new ArrayList<>();
if (temporaryDisks.size() != destinationStoragePools.size()) {
String warn = String.format("Discrepancy between the converted instance disks (%s) " +
"and the expected number of disks (%s)", temporaryDisks.size(), destinationStoragePools.size());
logger.warn(warn);
}
for (int i = 0; i < temporaryDisks.size(); i++) {
String poolPath = destinationStoragePools.get(i);
KVMStoragePool destinationPool = storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, poolPath);
if (destinationPool == null) {
String err = String.format("Could not find a storage pool by URI: %s", poolPath);
logger.error(err);
continue;
}
if (destinationPool.getType() != Storage.StoragePoolType.NetworkFilesystem) {
String err = String.format("Storage pool by URI: %s is not an NFS storage", poolPath);
logger.error(err);
continue;
}
KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
if (logger.isDebugEnabled()) {
String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" +
" to destination storage pool %s", i, sourceDisk.getPool().getLocalPath(), destinationPool.getUuid());
logger.debug(msg);
}
String destinationName = UUID.randomUUID().toString();
KVMPhysicalDisk destinationDisk = storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool, 7200 * 1000);
targetDisks.add(destinationDisk);
}
return targetDisks;
}
private UnmanagedInstanceTO getConvertedUnmanagedInstance(String baseName,
List<KVMPhysicalDisk> vmDisks,
LibvirtDomainXMLParser xmlParser) {
UnmanagedInstanceTO instanceTO = new UnmanagedInstanceTO();
instanceTO.setName(baseName);
instanceTO.setDisks(getUnmanagedInstanceDisks(vmDisks, xmlParser));
instanceTO.setNics(getUnmanagedInstanceNics(xmlParser));
return instanceTO;
}
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(LibvirtDomainXMLParser xmlParser) {
List<UnmanagedInstanceTO.Nic> nics = new ArrayList<>();
if (xmlParser != null) {
List<LibvirtVMDef.InterfaceDef> interfaces = xmlParser.getInterfaces();
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
nic.setMacAddress(interfaceDef.getMacAddress());
nic.setNicId(interfaceDef.getBrName());
nic.setAdapterType(interfaceDef.getModel().toString());
nics.add(nic);
}
}
return nics;
}
protected List<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<KVMPhysicalDisk> vmDisks, LibvirtDomainXMLParser xmlParser) {
List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
List<LibvirtVMDef.DiskDef> diskDefs = xmlParser != null ? xmlParser.getDisks() : null;
for (int i = 0; i< vmDisks.size(); i++) {
KVMPhysicalDisk physicalDisk = vmDisks.get(i);
KVMStoragePool storagePool = physicalDisk.getPool();
UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
disk.setPosition(i);
Pair<String, String> storagePoolHostAndPath = getNfsStoragePoolHostAndPath(storagePool);
disk.setDatastoreHost(storagePoolHostAndPath.first());
disk.setDatastorePath(storagePoolHostAndPath.second());
disk.setDatastoreName(storagePool.getUuid());
disk.setDatastoreType(storagePool.getType().name());
disk.setCapacity(physicalDisk.getVirtualSize());
disk.setFileBaseName(physicalDisk.getName());
if (CollectionUtils.isNotEmpty(diskDefs)) {
LibvirtVMDef.DiskDef diskDef = diskDefs.get(i);
disk.setController(diskDef.getBusType() != null ? diskDef.getBusType().toString() : LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
} else {
// If the job is finished but we cannot parse the XML, the guest VM can use the virtio driver
disk.setController(LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
}
instanceDisks.add(disk);
}
return instanceDisks;
}
protected Pair<String, String> getNfsStoragePoolHostAndPath(KVMStoragePool storagePool) {
String sourceHostIp = null;
String sourcePath = null;
List<String[]> commands = new ArrayList<>();
commands.add(new String[]{Script.getExecutableAbsolutePath("mount")});
commands.add(new String[]{Script.getExecutableAbsolutePath("grep"), storagePool.getLocalPath()});
String storagePoolMountPoint = Script.executePipedCommands(commands, 0).second();
logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint));
if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
String[] res = storagePoolMountPoint.strip().split(" ");
res = res[0].split(":");
if (res.length > 1) {
sourceHostIp = res[0].strip();
sourcePath = res[1].strip();
}
}
return new Pair<>(sourceHostIp, sourcePath);
}
protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException {
String xmlPath = String.format("%s.xml", installPath);
if (!new File(xmlPath).exists()) {
String err = String.format("Conversion failed. Unable to find the converted XML domain, expected %s", xmlPath);
logger.error(err);
throw new CloudRuntimeException(err);
}
InputStream is = new BufferedInputStream(new FileInputStream(xmlPath));
String xml = IOUtils.toString(is, Charset.defaultCharset());
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
try {
parser.parseDomainXML(xml);
return parser;
} catch (RuntimeException e) {
String err = String.format("Error parsing the converted instance XML domain at %s: %s", xmlPath, e.getMessage());
logger.error(err, e);
logger.debug(xml);
return null;
}
}
}

View File

@ -1119,11 +1119,12 @@ public class KVMStorageProcessor implements StorageProcessor {
storagePool = storagePoolMgr.getStoragePoolByURI(path);
}
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
isoPath = isoVol.getPath();
iso.defISODisk(isoPath, isUefiEnabled);
iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
} else {
iso.defISODisk(null, isUefiEnabled);
iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
}
final List<DiskDef> disks = resource.getDisks(conn, vmName);

View File

@ -173,7 +173,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* Checks if downloaded template is extractable
* @return true if it should be extracted, false if not
*/
private boolean isTemplateExtractable(String templatePath) {
public static boolean isTemplateExtractable(String templatePath) {
String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
}
@ -183,7 +183,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* @param downloadedTemplateFile
* @param templateUuid
*/
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
if (downloadedTemplateFile.endsWith(".zip")) {
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
} else if (downloadedTemplateFile.endsWith(".bz2")) {
@ -198,7 +198,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
/**
* Extract downloaded template into installPath, remove compressed file
*/
private void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
Script.runSimpleBashScript(extractCommand);
Script.runSimpleBashScript("rm -f " + downloadedTemplateFile);

View File

@ -18,7 +18,6 @@
//
package com.cloud.hypervisor.kvm.resource.wrapper;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -80,7 +79,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(storagePoolManager.getStoragePoolByURI(secondaryPoolUrl)).thenReturn(temporaryPool);
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2));
}
@Test
@ -107,51 +105,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Assert.assertNotNull(temporaryStoragePool);
}
@Test
public void testGetTemporaryDisksWithPrefixFromTemporaryPool() {
String convertPath = "/xyz";
String convertPrefix = UUID.randomUUID().toString();
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(physicalDisk1.getName()).thenReturn("disk1");
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(physicalDisk2.getName()).thenReturn("disk2");
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk1.getName()).thenReturn(String.format("%s-sda", convertPrefix));
KVMPhysicalDisk convertedDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk2.getName()).thenReturn(String.format("%s-sdb", convertPrefix));
KVMPhysicalDisk convertedXml = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedXml.getName()).thenReturn(String.format("%s.xml", convertPrefix));
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2,
convertedDisk1, convertedDisk2, convertedXml));
List<KVMPhysicalDisk> convertedDisks = convertInstanceCommandWrapper.getTemporaryDisksWithPrefixFromTemporaryPool(temporaryPool, convertPath, convertPrefix);
Assert.assertEquals(2, convertedDisks.size());
}
@Test
public void testGetTemporaryDisksFromParsedXml() {
String relativePath = UUID.randomUUID().toString();
String fullPath = String.format("/mnt/xyz/%s", relativePath);
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.VIRTIO;
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
diskDef.defFileBasedDisk(fullPath, "test", bus, type);
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk1.getName()).thenReturn("disk1");
Mockito.when(temporaryPool.getPhysicalDisk(relativePath)).thenReturn(convertedDisk1);
List<KVMPhysicalDisk> disks = convertInstanceCommandWrapper.getTemporaryDisksFromParsedXml(temporaryPool, parser, "");
Mockito.verify(convertInstanceCommandWrapper).sanitizeDisksPath(List.of(diskDef));
Assert.assertEquals(1, disks.size());
Assert.assertEquals("disk1", disks.get(0).getName());
}
@Test
public void testSanitizeDisksPath() {
String relativePath = UUID.randomUUID().toString();

View File

@ -0,0 +1,245 @@
//
// 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.hypervisor.kvm.resource.wrapper;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ImportConvertedInstanceCommand;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.RemoteInstanceTO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
import com.cloud.storage.Storage;
import com.cloud.utils.Pair;
import com.cloud.utils.script.Script;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@RunWith(MockitoJUnitRunner.class)
public class LibvirtImportConvertedInstanceCommandWrapperTest {
@Spy
private LibvirtImportConvertedInstanceCommandWrapper importInstanceCommandWrapper =
Mockito.spy(LibvirtImportConvertedInstanceCommandWrapper.class);
@Mock
private LibvirtComputingResource libvirtComputingResourceMock;
@Mock
private KVMStoragePool temporaryPool;
@Mock
private KVMStoragePool destinationPool;
@Mock
private PrimaryDataStoreTO primaryDataStore;
@Mock
private NfsTO secondaryDataStore;
@Mock
private KVMStoragePoolManager storagePoolManager;
private static final String secondaryPoolUrl = "nfs://192.168.1.1/secondary";
private static final String vmName = "VmToImport";
@Before
public void setUp() {
Mockito.when(secondaryDataStore.getUrl()).thenReturn(secondaryPoolUrl);
Mockito.when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager);
Mockito.when(storagePoolManager.getStoragePoolByURI(secondaryPoolUrl)).thenReturn(temporaryPool);
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2));
}
@Test
public void testGetTemporaryStoragePool() {
KVMStoragePool temporaryStoragePool = importInstanceCommandWrapper.getTemporaryStoragePool(secondaryDataStore, libvirtComputingResourceMock.getStoragePoolMgr());
Assert.assertNotNull(temporaryStoragePool);
}
@Test
public void testGetTemporaryDisksWithPrefixFromTemporaryPool() {
String convertPath = "/xyz";
String convertPrefix = UUID.randomUUID().toString();
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(physicalDisk1.getName()).thenReturn("disk1");
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(physicalDisk2.getName()).thenReturn("disk2");
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk1.getName()).thenReturn(String.format("%s-sda", convertPrefix));
KVMPhysicalDisk convertedDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk2.getName()).thenReturn(String.format("%s-sdb", convertPrefix));
KVMPhysicalDisk convertedXml = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedXml.getName()).thenReturn(String.format("%s.xml", convertPrefix));
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2,
convertedDisk1, convertedDisk2, convertedXml));
List<KVMPhysicalDisk> convertedDisks = importInstanceCommandWrapper.getTemporaryDisksWithPrefixFromTemporaryPool(temporaryPool, convertPath, convertPrefix);
Assert.assertEquals(2, convertedDisks.size());
}
@Test
public void testGetTemporaryDisksFromParsedXml() {
String relativePath = UUID.randomUUID().toString();
String fullPath = String.format("/mnt/xyz/%s", relativePath);
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.VIRTIO;
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
diskDef.defFileBasedDisk(fullPath, "test", bus, type);
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(convertedDisk1.getName()).thenReturn("disk1");
Mockito.when(temporaryPool.getPhysicalDisk(relativePath)).thenReturn(convertedDisk1);
List<KVMPhysicalDisk> disks = importInstanceCommandWrapper.getTemporaryDisksFromParsedXml(temporaryPool, parser, "");
Mockito.verify(importInstanceCommandWrapper).sanitizeDisksPath(List.of(diskDef));
Assert.assertEquals(1, disks.size());
Assert.assertEquals("disk1", disks.get(0).getName());
}
@Test
public void testSanitizeDisksPath() {
String relativePath = UUID.randomUUID().toString();
String fullPath = String.format("/mnt/xyz/%s", relativePath);
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.VIRTIO;
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
diskDef.defFileBasedDisk(fullPath, "test", bus, type);
importInstanceCommandWrapper.sanitizeDisksPath(List.of(diskDef));
Assert.assertEquals(relativePath, diskDef.getDiskPath());
}
@Test
public void testMoveTemporaryDisksToDestination() {
KVMPhysicalDisk sourceDisk = Mockito.mock(KVMPhysicalDisk.class);
Mockito.lenient().when(sourceDisk.getPool()).thenReturn(temporaryPool);
List<KVMPhysicalDisk> disks = List.of(sourceDisk);
String destinationPoolUuid = UUID.randomUUID().toString();
List<String> destinationPools = List.of(destinationPoolUuid);
KVMPhysicalDisk destDisk = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(destDisk.getPath()).thenReturn("xyz");
Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid))
.thenReturn(destinationPool);
Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt()))
.thenReturn(destDisk);
List<KVMPhysicalDisk> movedDisks = importInstanceCommandWrapper.moveTemporaryDisksToDestination(disks, destinationPools, storagePoolManager);
Assert.assertEquals(1, movedDisks.size());
Assert.assertEquals("xyz", movedDisks.get(0).getPath());
}
@Test
public void testGetUnmanagedInstanceDisks() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
String relativePath = UUID.randomUUID().toString();
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.IDE;
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
diskDef.defFileBasedDisk(relativePath, relativePath, bus, type);
KVMPhysicalDisk sourceDisk = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(sourceDisk.getName()).thenReturn(UUID.randomUUID().toString());
Mockito.when(sourceDisk.getPool()).thenReturn(destinationPool);
Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
List<KVMPhysicalDisk> disks = List.of(sourceDisk);
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
Mockito.doReturn(new Pair<String, String>(null, null)).when(importInstanceCommandWrapper).getNfsStoragePoolHostAndPath(destinationPool);
Mockito.when(Script.executePipedCommands(Mockito.anyList(), Mockito.anyLong()))
.thenReturn(new Pair<>(0, null));
List<UnmanagedInstanceTO.Disk> unmanagedInstanceDisks = importInstanceCommandWrapper.getUnmanagedInstanceDisks(disks, parser);
Assert.assertEquals(1, unmanagedInstanceDisks.size());
UnmanagedInstanceTO.Disk disk = unmanagedInstanceDisks.get(0);
Assert.assertEquals(LibvirtVMDef.DiskDef.DiskBus.IDE.toString(), disk.getController());
}
}
@Test
public void testGetNfsStoragePoolHostAndPath() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
String localMountPoint = "/mnt/xyz";
String host = "192.168.1.2";
String path = "/secondary";
String mountOutput = String.format("%s:%s on %s type nfs (...)", host, path, localMountPoint);
Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint);
Mockito.when(Script.executePipedCommands(Mockito.anyList(), Mockito.anyLong()))
.thenReturn(new Pair<>(0, mountOutput));
Pair<String, String> pair = importInstanceCommandWrapper.getNfsStoragePoolHostAndPath(temporaryPool);
Assert.assertEquals(host, pair.first());
Assert.assertEquals(path, pair.second());
}
}
private RemoteInstanceTO getRemoteInstanceTO(Hypervisor.HypervisorType hypervisorType) {
RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class);
Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType);
Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName);
return remoteInstanceTO;
}
private ImportConvertedInstanceCommand getImportConvertedInstanceCommand(RemoteInstanceTO remoteInstanceTO) {
ImportConvertedInstanceCommand cmd = Mockito.mock(ImportConvertedInstanceCommand.class);
Mockito.when(cmd.getSourceInstance()).thenReturn(remoteInstanceTO);
Mockito.when(cmd.getConversionTemporaryLocation()).thenReturn(secondaryDataStore);
return cmd;
}
@Test
public void testExecuteConvertUnsupportedOnTheHost() {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
ImportConvertedInstanceCommand cmd = getImportConvertedInstanceCommand(remoteInstanceTO);
Answer answer = importInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult());
}
@Test
public void testExecuteConvertUnsupportedHypervisors() {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.XenServer);
ImportConvertedInstanceCommand cmd = getImportConvertedInstanceCommand(remoteInstanceTO);
Answer answer = importInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult());
}
}

View File

@ -461,7 +461,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
sourcePort, sourcePort,
vmIp,
destPort, destPort,
"tcp", networkId, accountId, domainId, vmId);
"tcp", networkId, accountId, domainId, vmId, null);
newRule.setDisplay(true);
newRule.setState(FirewallRule.State.Add);
newRule = portForwardingRulesDao.persist(newRule);

View File

@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Disable discard="unmap" for ide devices and qemu < 7.0
https://bugzilla.redhat.com/show_bug.cgi?id=2029980
## [2024-10-14]
### Added
- Support for ISO direct download to primary storage
## [2024-10-04]
### Added

View File

@ -23,11 +23,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nonnull;
import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
import org.apache.cloudstack.utils.qemu.QemuImg;
@ -57,6 +59,8 @@ import com.linbit.linstor.api.model.StoragePool;
import com.linbit.linstor.api.model.Volume;
import com.linbit.linstor.api.model.VolumeDefinition;
import java.io.File;
public class LinstorStorageAdaptor implements StorageAdaptor {
protected Logger logger = LogManager.getLogger(getClass());
private static final Map<String, KVMStoragePool> MapStorageUuidToStoragePool = new HashMap<>();
@ -566,13 +570,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
final DevelopersApi api = getLinstorAPI(destPools);
final String rscName = LinstorUtil.RSC_PREFIX + name;
try {
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
} catch (ApiException apiExc) {
logger.error("Error setting aux properties for {}", rscName);
logLinstorAnswers(apiExc.getApiCallRcList());
}
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath());
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@ -623,13 +621,58 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
return null;
}
private void fileExistsOrThrow(String templateFilePath) {
File sourceFile = new File(templateFilePath);
if (!sourceFile.exists()) {
throw new CloudRuntimeException("Direct download template file " + sourceFile +
" does not exist on this host");
}
}
private String getFinalDirectDownloadPath(String templateFilePath, KVMStoragePool destPool) {
String finalSourcePath = templateFilePath;
if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) {
finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.'));
LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath);
}
return finalSourcePath;
}
private void applyAuxProps(DevelopersApi api, String csPath, String csName, String csVMName) {
final String rscName = getLinstorRscName(csPath);
try {
LinstorUtil.applyAuxProps(api, rscName, csName, csVMName);
} catch (ApiException apiExc) {
logger.error(String.format("Error setting aux properties for %s", rscName));
logLinstorAnswers(apiExc.getApiCallRcList());
}
}
@Override
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
KVMStoragePool destPool, Storage.ImageFormat format,
int timeout)
{
logger.debug("Linstor: createTemplateFromDirectDownloadFile");
return null;
fileExistsOrThrow(templateFilePath);
String name = UUID.randomUUID().toString();
String finalSourcePath = getFinalDirectDownloadPath(templateFilePath, destPool);
File finalSourceFile = new File(finalSourcePath);
final KVMPhysicalDisk dstDisk = destPool.createPhysicalDisk(
name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, finalSourceFile.length(), null);
final DevelopersApi api = getLinstorAPI(destPool);
applyAuxProps(api, name, finalSourceFile.getName(), null);
Script.runSimpleBashScript(
String.format("dd if=\"%s\" of=\"%s\" bs=64k conv=nocreat,sparse oflag=direct",
finalSourcePath, dstDisk.getPath()));
Script.runSimpleBashScript("rm " + finalSourcePath);
return dstDisk;
}
public long getCapacity(LinstorStoragePool pool) {

View File

@ -30,6 +30,9 @@ import java.util.Set;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import com.cloud.dc.DataCenter;
import com.cloud.network.dao.NsxProviderDao;
import com.cloud.network.element.NsxProviderVO;
@ -40,7 +43,6 @@ import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.springframework.stereotype.Component;
import com.cloud.configuration.Config;
import com.cloud.domain.dao.DomainDao;
@ -402,20 +404,34 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol()
.equalsIgnoreCase(rule.getProtocol()))));
// if both rules are firewall and their cidrs are different, we can skip port ranges verification
boolean bothRulesFirewall = (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall);
// if both rules are firewall/port forwarding and their cidrs are different, we can skip port ranges verification
boolean duplicatedCidrs = false;
boolean bothRulesFirewall = (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall);
if (bothRulesFirewall) {
_firewallDao.loadSourceCidrs(rule);
_firewallDao.loadSourceCidrs((FirewallRuleVO)newRule);
if (ObjectUtils.anyNull(rule.getSourceCidrList(), newRule.getSourceCidrList())) {
continue;
}
_firewallDao.loadDestinationCidrs(rule);
_firewallDao.loadDestinationCidrs((FirewallRuleVO) newRule);
if (rule.getSourceCidrList() == null || newRule.getSourceCidrList() == null) {
duplicatedCidrs = detectConflictingCidrs(rule.getSourceCidrList(), newRule.getSourceCidrList()) && detectConflictingCidrs(rule.getDestinationCidrList(), newRule.getDestinationCidrList());
}
boolean bothRulesPortForwarding = rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.PortForwarding;
if (bothRulesPortForwarding) {
_firewallDao.loadSourceCidrs(rule);
_firewallDao.loadSourceCidrs((FirewallRuleVO) newRule);
if (ObjectUtils.anyNull(rule.getSourceCidrList(), newRule.getSourceCidrList())) {
continue;
}
duplicatedCidrs = (detectConflictingCidrs(rule.getSourceCidrList(), newRule.getSourceCidrList()) && detectConflictingCidrs(rule.getDestinationCidrList(), newRule.getDestinationCidrList()));
duplicatedCidrs = detectConflictingCidrs(rule.getSourceCidrList(), newRule.getSourceCidrList());
}
if (!oneOfRulesIsFirewall) {
@ -451,18 +467,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
if (!notNullPorts) {
continue;
} else if (!oneOfRulesIsFirewall &&
!(bothRulesFirewall && !duplicatedCidrs) &&
((rule.getSourcePortStart().intValue() <= newRule.getSourcePortStart().intValue() &&
rule.getSourcePortEnd().intValue() >= newRule.getSourcePortStart().intValue()) ||
(rule.getSourcePortStart().intValue() <= newRule.getSourcePortEnd().intValue() &&
rule.getSourcePortEnd().intValue() >= newRule.getSourcePortEnd().intValue()) ||
(newRule.getSourcePortStart().intValue() <= rule.getSourcePortStart().intValue() &&
newRule.getSourcePortEnd().intValue() >= rule.getSourcePortStart().intValue()) ||
(newRule.getSourcePortStart().intValue() <= rule.getSourcePortEnd().intValue() &&
newRule.getSourcePortEnd().intValue() >= rule.getSourcePortEnd().intValue()))) {
//Above else if conditions checks for the conflicting port ranges.
} else if (checkIfRulesHaveConflictingPortRanges(newRule, rule, oneOfRulesIsFirewall, bothRulesFirewall, bothRulesPortForwarding, duplicatedCidrs)) {
// we allow port forwarding rules with the same parameters but different protocols
boolean allowPf =
(rule.getPurpose() == Purpose.PortForwarding && newRule.getPurpose() == Purpose.PortForwarding && !newRule.getProtocol().equalsIgnoreCase(
@ -489,6 +494,45 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
}
}
protected boolean checkIfRulesHaveConflictingPortRanges(FirewallRule newRule, FirewallRule rule, boolean oneOfRulesIsFirewall, boolean bothRulesFirewall, boolean bothRulesPortForwarding, boolean duplicatedCidrs) {
String rulesAsString = String.format("[%s] and [%s]", rule, newRule);
if (oneOfRulesIsFirewall) {
logger.debug(String.format("Only one of the rules (%s) is firewall; therefore, their port ranges will not conflict.",
rulesAsString));
return false;
}
if ((bothRulesFirewall || bothRulesPortForwarding) && !duplicatedCidrs) {
logger.debug(String.format("Both rules (%s) are firewall/port forwarding, but they do not have duplicated CIDRs; therefore, their port ranges will not conflict.",
rulesAsString));
return false;
}
if (rule.getSourcePortStart() <= newRule.getSourcePortStart() && rule.getSourcePortEnd() >= newRule.getSourcePortStart()) {
logger.debug(String.format("Rules (%s) have conflicting port ranges.", rulesAsString));
return true;
}
if (rule.getSourcePortStart() <= newRule.getSourcePortEnd() && rule.getSourcePortEnd() >= newRule.getSourcePortEnd()) {
logger.debug(String.format("Rules (%s) have conflicting port ranges.", rulesAsString));
return true;
}
if (newRule.getSourcePortStart() <= rule.getSourcePortStart() && newRule.getSourcePortEnd() >= rule.getSourcePortStart()) {
logger.debug(String.format("Rules (%s) have conflicting port ranges.", rulesAsString));
return true;
}
if (newRule.getSourcePortStart() <= rule.getSourcePortEnd() && newRule.getSourcePortEnd() >= rule.getSourcePortEnd()) {
logger.debug(String.format("Rules (%s) have conflicting port ranges.", rulesAsString));
return true;
}
logger.debug(String.format("Rules (%s) do not have conflicting port ranges.", rulesAsString));
return false;
}
@Override
public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type,
Long networkId, FirewallRule.TrafficType trafficType) {

View File

@ -28,6 +28,7 @@ import java.util.Set;
import javax.inject.Inject;
import com.cloud.network.rules.PortForwardingRuleVO;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@ -408,6 +409,7 @@ public class CommandSetupHelper {
final List<PortForwardingRuleTO> rulesTO = new ArrayList<PortForwardingRuleTO>();
if (rules != null) {
for (final PortForwardingRule rule : rules) {
_rulesDao.loadSourceCidrs((PortForwardingRuleVO) rule);
final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
final PortForwardingRuleTO ruleTO = new PortForwardingRuleTO(rule, null, sourceIp.getAddress().addr());
rulesTO.add(ruleTO);

View File

@ -33,6 +33,7 @@ import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VirtualMachineProfileImpl;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.command.user.firewall.ListPortForwardingRulesCmd;
import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@ -102,6 +103,7 @@ import com.cloud.vm.dao.NicSecondaryIpDao;
import com.cloud.vm.dao.NicSecondaryIpVO;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.commons.collections.CollectionUtils;
public class RulesManagerImpl extends ManagerBase implements RulesManager, RulesService {
@ -112,7 +114,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
@Inject
PortForwardingRulesDao _portForwardingDao;
@Inject
FirewallRulesCidrsDao _firewallCidrsDao;
FirewallRulesCidrsDao firewallCidrsDao;
@Inject
FirewallRulesDao _firewallDao;
@Inject
@ -247,6 +249,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
final Long accountId = ipAddress.getAllocatedToAccountId();
final Long domainId = ipAddress.getAllocatedInDomainId();
List<String> sourceCidrList = rule.getSourceCidrList();
// start port can't be bigger than end port
if (rule.getDestinationPortStart() > rule.getDestinationPortEnd()) {
@ -310,9 +313,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
final IPAddressVO ipAddressFinal = ipAddress;
return Transaction.execute((TransactionCallbackWithException<PortForwardingRuleVO, NetworkRuleConflictException>) status -> {
PortForwardingRuleVO newRule =
new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), dstIpFinal,
rule.getDestinationPortStart(), rule.getDestinationPortEnd(), rule.getProtocol().toLowerCase(), networkId, accountId, domainId, vmId);
new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), dstIpFinal,
rule.getDestinationPortStart(), rule.getDestinationPortEnd(), rule.getProtocol().toLowerCase(), networkId, accountId, domainId, vmId, sourceCidrList);
if (forDisplay != null) {
newRule.setDisplay(forDisplay);
}
@ -896,6 +898,10 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
_accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()]));
}
for (PortForwardingRuleVO rule : rules) {
rule.setSourceCidrList(firewallCidrsDao.getSourceCidrs(rule.getId()));
}
try {
if (!_firewallMgr.applyRules(rules, continueOnError, true)) {
return false;
@ -949,6 +955,10 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
_accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()]));
}
for (PortForwardingRuleVO rule: rules) {
rule.setSourceCidrList(firewallCidrsDao.getSourceCidrs(rule.getId()));
}
try {
if (!_firewallMgr.applyRules(rules, continueOnError, true)) {
return false;
@ -1568,12 +1578,22 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
@Override
@ActionEvent(eventType = EventTypes.EVENT_NET_RULE_MODIFY, eventDescription = "updating forwarding rule", async = true)
public PortForwardingRule updatePortForwardingRule(long id, Integer privatePort, Integer privateEndPort, Long virtualMachineId, Ip vmGuestIp, String customId, Boolean forDisplay) {
Account caller = CallContext.current().getCallingAccount();
public PortForwardingRule updatePortForwardingRule(UpdatePortForwardingRuleCmd cmd) {
long id = cmd.getId();
Integer privatePort = cmd.getPrivatePort();
Integer privateEndPort = cmd.getPrivateEndPort();
Long virtualMachineId = cmd.getVirtualMachineId();
Ip vmGuestIp = cmd.getVmGuestIp();
String customId = cmd.getCustomId();
Boolean forDisplay = cmd.getDisplay();
List<String> sourceCidrList = cmd.getSourceCidrList();
PortForwardingRuleVO rule = _portForwardingDao.findById(id);
if (rule == null) {
throw new InvalidParameterValueException("Unable to find " + id);
}
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, rule);
if (customId != null) {
@ -1634,6 +1654,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
}
}
validatePortForwardingSourceCidrList(sourceCidrList);
// revoke old rules at first
List<PortForwardingRuleVO> rules = new ArrayList<PortForwardingRuleVO>();
rule.setState(State.Revoke);
@ -1661,6 +1683,11 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
rule.setVirtualMachineId(virtualMachineId);
rule.setDestinationIpAddress(dstIp);
}
if (sourceCidrList != null) {
rule.setSourceCidrList(sourceCidrList);
}
_portForwardingDao.update(id, rule);
//apply new rules
@ -1670,4 +1697,17 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
return _portForwardingDao.findById(id);
}
@Override
public void validatePortForwardingSourceCidrList(List<String> sourceCidrList) {
if (CollectionUtils.isEmpty(sourceCidrList)) {
return;
}
for (String cidr : sourceCidrList) {
if (!NetUtils.isValidCidrList(cidr) && !NetUtils.isValidIp6Cidr(cidr)) {
throw new InvalidParameterValueException(String.format("The given source CIDR [%s] is invalid.", cidr));
}
}
}
}

View File

@ -31,6 +31,8 @@ import com.cloud.agent.api.GetRemoteVmsAnswer;
import com.cloud.agent.api.GetRemoteVmsCommand;
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
import com.cloud.agent.api.ImportConvertedInstanceAnswer;
import com.cloud.agent.api.ImportConvertedInstanceCommand;
import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
import com.cloud.agent.api.to.DataStoreTO;
@ -1338,6 +1340,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
Long clusterId = cmd.getClusterId();
Cluster cluster = basicAccessChecks(clusterId);
if (!cluster.getAllocationState().equals(Cluster.AllocationState.Enabled)) {
throw new InvalidParameterValueException(String.format("Cluster [%s] is not enabled.", cluster));
}
final Account caller = CallContext.current().getCallingAccount();
final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId());
final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
@ -1601,6 +1607,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
String clusterName = cmd.getClusterName();
String sourceHostName = cmd.getHostIp();
Long convertInstanceHostId = cmd.getConvertInstanceHostId();
Long importInstanceHostId = cmd.getImportInstanceHostId();
Long convertStoragePoolId = cmd.getConvertStoragePoolId();
if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) {
@ -1630,12 +1637,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
DataStoreTO temporaryConvertLocation = null;
String ovfTemplateOnConvertLocation = null;
try {
HostVO convertHost = selectInstanceConversionKVMHostInCluster(destinationCluster, convertInstanceHostId);
HostVO convertHost = selectKVMHostForConversionInCluster(destinationCluster, convertInstanceHostId);
HostVO importHost = selectKVMHostForImportingInCluster(destinationCluster, importInstanceHostId);
CheckConvertInstanceAnswer conversionSupportAnswer = checkConversionSupportOnHost(convertHost, sourceVMName, false);
logger.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
" from VMware to KVM ", convertHost.getId(), convertHost.getName(), sourceVMName));
temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId);
temporaryConvertLocation = selectInstanceConversionTemporaryLocation(
destinationCluster, convertHost, convertStoragePoolId);
List<StoragePoolVO> convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster);
long importStartTime = System.currentTimeMillis();
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName);
@ -1654,11 +1663,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value();
ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads);
convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName,
sourceVMwareInstance, convertHost, importHost, convertStoragePools,
temporaryConvertLocation, ovfTemplateOnConvertLocation);
} else {
// Uses KVM Host for OVF export to temporary conversion location, through ovftool
convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(
sourceVMName, sourceVMwareInstance, convertHost, importHost, convertStoragePools,
temporaryConvertLocation, vcenter, username, password, datacenterName);
}
@ -1814,23 +1825,81 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return params;
}
private HostVO selectInstanceConversionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
if (convertInstanceHostId != null) {
HostVO selectedHost = hostDao.findById(convertInstanceHostId);
HostVO selectKVMHostForImportingInCluster(Cluster destinationCluster, Long importInstanceHostId) {
if (importInstanceHostId != null) {
String err = null;
HostVO selectedHost = hostDao.findById(importInstanceHostId);
if (selectedHost == null) {
String msg = String.format("Cannot find host with ID %s", convertInstanceHostId);
logger.error(msg);
throw new CloudRuntimeException(msg);
err = String.format("Cannot find host with ID %s to import the instance",
importInstanceHostId);
} else if (selectedHost.getResourceState() != ResourceState.Enabled) {
err = String.format(
"Cannot import the converted instance on the host %s as it is not in Enabled state",
selectedHost.getName());
} else if (selectedHost.getStatus() != Status.Up) {
err = String.format(
"Cannot import the converted instance on the host %s as it is not running",
selectedHost.getName());
} else if (selectedHost.getType() != Host.Type.Routing) {
err = String.format(
"Cannot import the converted instance on the host %s as it is not a routing host",
selectedHost.getName());
} else if (destinationCluster.getId() != selectedHost.getClusterId()) {
err = String.format(
"Cannot import the converted instance on the host %s as it is not in the same cluster as the destination cluster",
selectedHost.getName());
}
if (selectedHost.getResourceState() != ResourceState.Enabled ||
selectedHost.getStatus() != Status.Up || selectedHost.getType() != Host.Type.Routing ||
selectedHost.getClusterId() != destinationCluster.getId()) {
String msg = String.format("Cannot perform the conversion on the host %s as it is not a running and Enabled host", selectedHost.getName());
logger.error(msg);
throw new CloudRuntimeException(msg);
if (err != null) {
logger.error(err);
throw new CloudRuntimeException(err);
}
return selectedHost;
}
List<HostVO> hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
if (CollectionUtils.isNotEmpty(hosts)) {
return hosts.get(new Random().nextInt(hosts.size()));
}
String err = String.format(
"Could not find any suitable %s host in cluster %s to import the converted instance",
destinationCluster.getHypervisorType(), destinationCluster.getName());
logger.error(err);
throw new CloudRuntimeException(err);
}
HostVO selectKVMHostForConversionInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
if (convertInstanceHostId != null) {
HostVO selectedHost = hostDao.findById(convertInstanceHostId);
String err = null;
if (selectedHost == null) {
err = String.format("Cannot find host with ID %s for conversion",
convertInstanceHostId);
} else if (!List.of(ResourceState.Enabled, ResourceState.Disabled).contains(selectedHost.getResourceState())) {
err = String.format(
"Cannot perform the conversion on the host %s as the host is in %s state",
selectedHost.getName(), selectedHost.getResourceState());
} else if (selectedHost.getStatus() != Status.Up) {
err = String.format(
"Cannot perform the conversion on the host %s as it is not running",
selectedHost.getName());
} else if (selectedHost.getType() != Host.Type.Routing) {
err = String.format(
"Cannot perform the conversion on the host %s as it is not a routing host",
selectedHost.getName());
} else if (destinationCluster.getDataCenterId() != selectedHost.getDataCenterId()) {
err = String.format(
"Cannot perform the conversion on the host %s as it is not in the same zone as the destination cluster",
selectedHost.getName());
}
if (err != null) {
logger.error(err);
throw new CloudRuntimeException(err);
}
return selectedHost;
}
// Auto select host with conversion capability
List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION);
if (CollectionUtils.isNotEmpty(hosts)) {
@ -1875,10 +1944,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return checkConvertInstanceAnswer;
}
private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
String ovfTemplateDirConvertLocation) {
logger.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore",
private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(
String sourceVM, UnmanagedInstanceTO sourceVMwareInstance,
HostVO convertHost, HostVO importHost,
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
String ovfTemplateDirConvertLocation
) {
logger.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore",
sourceVM, convertHost.getId(), convertHost.getName(), ovfTemplateDirConvertLocation));
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM);
@ -1907,9 +1979,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
}
private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(
String sourceVM, UnmanagedInstanceTO sourceVMwareInstance,
HostVO convertHost, HostVO importHost, List<StoragePoolVO> convertStoragePools,
DataStoreTO temporaryConvertLocation, String vcenterHost,
String vcenterUsername, String vcenterPassword, String datacenterName
) {
logger.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) after OVF export through ovftool",
sourceVM, convertHost.getId(), convertHost.getName()));
@ -1926,9 +2001,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
cmd.setThreadsCountToExportOvf(noOfThreads);
return convertAndImportToKVM(cmd, convertHost, importHost, sourceVM,
remoteInstanceTO, destinationStoragePools, temporaryConvertLocation);
}
private UnmanagedInstanceTO convertAndImportToKVM(ConvertInstanceCommand convertInstanceCommand, HostVO convertHost, HostVO importHost,
String sourceVM,
RemoteInstanceTO remoteInstanceTO,
List<String> destinationStoragePools,
DataStoreTO temporaryConvertLocation) {
Answer convertAnswer;
try {
convertAnswer = agentManager.send(convertHost.getId(), cmd);
convertAnswer = agentManager.send(convertHost.getId(), convertInstanceCommand);
} catch (AgentUnavailableException | OperationTimedoutException e) {
String err = String.format("Could not send the convert instance command to host %s (%s) due to: %s",
convertHost.getId(), convertHost.getName(), e.getMessage());
@ -1942,7 +2026,30 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
logger.error(err);
throw new CloudRuntimeException(err);
}
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
Answer importAnswer;
try {
ImportConvertedInstanceCommand importCmd = new ImportConvertedInstanceCommand(
remoteInstanceTO, destinationStoragePools, temporaryConvertLocation,
((ConvertInstanceAnswer)convertAnswer).getTemporaryConvertUuid());
importAnswer = agentManager.send(importHost.getId(), importCmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
String err = String.format(
"Could not send the import converted instance command to host %d (%s) due to: %s",
importHost.getId(), importHost.getName(), e.getMessage());
logger.error(err, e);
throw new CloudRuntimeException(err);
}
if (!importAnswer.getResult()) {
String err = String.format(
"The import process failed for instance %s from VMware to KVM on host %s: %s",
sourceVM, importHost.getName(), importAnswer.getDetails());
logger.error(err);
throw new CloudRuntimeException(err);
}
return ((ImportConvertedInstanceAnswer) importAnswer).getConvertedInstance();
}
private List<StoragePoolVO> findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) {
@ -1974,7 +2081,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new CloudRuntimeException(msg);
}
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId) {
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster,
HostVO convertHost,
Long convertStoragePoolId) {
if (convertStoragePoolId != null) {
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
if (selectedStoragePool == null) {
@ -1985,6 +2094,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
logFailureAndThrowException(String.format("Cannot use the storage pool %s for the instance conversion as " +
"it is not in the scope of the cluster %s", selectedStoragePool.getName(), destinationCluster.getName()));
}
if (convertHost != null && selectedStoragePool.getScope() == ScopeType.CLUSTER && !selectedStoragePool.getClusterId().equals(convertHost.getClusterId())) {
logFailureAndThrowException(String.format("Cannot use the storage pool %s for the instance conversion as " +
"the host %s for conversion is in a different cluster", selectedStoragePool.getName(), convertHost.getName()));
}
if (selectedStoragePool.getScope() == ScopeType.HOST) {
logFailureAndThrowException(String.format("The storage pool %s is a local storage pool and not supported for temporary conversion location, cluster and zone wide NFS storage pools are supported", selectedStoragePool.getName()));
} else if (selectedStoragePool.getPoolType() != Storage.StoragePoolType.NetworkFilesystem) {

View File

@ -27,7 +27,6 @@ import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.element.FirewallServiceProvider;
import com.cloud.network.element.VirtualRouterElement;
import com.cloud.network.element.VpcVirtualRouterElement;
import com.cloud.network.rules.FirewallManager;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRule.Purpose;
import com.cloud.network.rules.FirewallRuleVO;
@ -35,11 +34,9 @@ import com.cloud.network.vpc.VpcManager;
import com.cloud.user.AccountManager;
import com.cloud.user.DomainManager;
import com.cloud.utils.component.ComponentContext;
import junit.framework.Assert;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@ -47,6 +44,7 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
@ -62,40 +60,8 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class FirewallManagerTest {
private Logger logger = LogManager.getLogger(FirewallManagerTest.class);
private AutoCloseable closeable;
@Ignore("Requires database to be set up")
@Test
public void testInjected() {
// FirewallManagerImpl firewallMgr = (FirewallManagerImpl)ComponentLocator.getCurrentLocator().getManager(FirewallManager.class);
// Assert.assertTrue(firewallMgr._firewallElements.enumeration().hasMoreElements());
// Assert.assertTrue(firewallMgr._pfElements.enumeration().hasMoreElements());
// Assert.assertTrue(firewallMgr._staticNatElements.enumeration().hasMoreElements());
// Assert.assertTrue(firewallMgr._networkAclElements.enumeration().hasMoreElements());
// Assert.assertNotNull(firewallMgr._networkModel);
//
// Assert.assertNotNull(firewallMgr._firewallElements.get("VirtualRouter"));
// Assert.assertNotNull(firewallMgr._firewallElements.get("VpcVirtualRouter"));
// Assert.assertNotNull(firewallMgr._pfElements.get("VirtualRouter"));
// Assert.assertNotNull(firewallMgr._pfElements.get("VpcVirtualRouter"));
// Assert.assertNotNull(firewallMgr._staticNatElements.get("VirtualRouter"));
// Assert.assertNotNull(firewallMgr._staticNatElements.get("VpcVirtualRouter"));
// Assert.assertNotNull(firewallMgr._networkAclElements.get("VpcVirtualRouter"));
// Assert.assertNull(firewallMgr._networkAclElements.get("VirtualRouter"));
//
//
// Assert.assertTrue(firewallMgr._firewallElements.get("VirtualRouter") instanceof FirewallServiceProvider);
// Assert.assertTrue(firewallMgr._pfElements.get("VirtualRouter") instanceof PortForwardingServiceProvider);
// Assert.assertTrue(firewallMgr._staticNatElements.get("VirtualRouter") instanceof StaticNatServiceProvider);
// Assert.assertTrue(firewallMgr._networkAclElements.get("VpcVirtualRouter") instanceof NetworkACLServiceProvider);
logger.info("Done testing injection of service elements into firewall manager");
}
@Mock
AccountManager _accountMgr;
@Mock
@ -111,12 +77,30 @@ public class FirewallManagerTest {
@Mock
FirewallRulesDao _firewallDao;
@Spy
@InjectMocks
FirewallManager _firewallMgr = new FirewallManagerImpl();
FirewallManagerImpl _firewallMgr;
FirewallRule fwRule50to150;
FirewallRule fwRule100to200;
FirewallRule fwRule151to200;
FirewallRule pfRule50to150;
FirewallRule pfRule100to200;
FirewallRule pfRule151to200;
@Before
public void initMocks() {
closeable = MockitoAnnotations.openMocks(this);
fwRule50to150 = createFirewallRule(50, 150, Purpose.Firewall);
fwRule100to200 = createFirewallRule(100, 150, Purpose.Firewall);
fwRule151to200 = createFirewallRule(151, 200, Purpose.Firewall);
pfRule50to150 = createFirewallRule(50, 150, Purpose.PortForwarding);
pfRule100to200 = createFirewallRule(100, 150, Purpose.PortForwarding);
pfRule151to200 = createFirewallRule(151, 200, Purpose.PortForwarding);
}
@After
@ -124,6 +108,11 @@ public class FirewallManagerTest {
closeable.close();
}
private FirewallRule createFirewallRule(int startPort, int endPort, Purpose purpose) {
return new FirewallRuleVO("xid", 1L, startPort, endPort, "TCP", 2, 3, 4, purpose, new ArrayList<>(),
new ArrayList<>(), 5, 6, null, FirewallRule.TrafficType.Ingress);
}
@Ignore("Requires database to be set up")
@Test
public void testApplyRules() {
@ -218,6 +207,75 @@ public class FirewallManagerTest {
}
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestOnlyOneRuleIsFirewallReturnsFalse()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(fwRule50to150, pfRule50to150, true, false, false, true);
Assert.assertFalse(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesAreFirewallButNoDuplicateCidrsReturnsFalse()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(fwRule50to150, fwRule50to150, false, true, false, false);
Assert.assertFalse(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesArePortForwardingButNoDuplicateCidrsReturnsFalse()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(pfRule50to150, pfRule50to150, false, false, true, false);
Assert.assertFalse(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesAreFirewallAndDuplicatedCidrsAndNewRuleSourceStartPortIsInsideExistingRangeReturnsTrue()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(fwRule100to200, fwRule50to150, false, true, false, true);
Assert.assertTrue(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesAreFirewallAndDuplicatedCidrsAndNewRuleSourceEndPortIsInsideExistingRangeReturnsTrue()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(fwRule50to150, fwRule100to200, false, true, false, true);
Assert.assertTrue(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesArePortForwardingAndDuplicatedCidrsAndNewRuleSourceStartPortIsInsideExistingRangeReturnsTrue()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(pfRule50to150, pfRule100to200, false, false, true, true);
Assert.assertTrue(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesArePortForwardingAndDuplicatedCidrsAndNewRuleSourceEndPortIsInsideExistingRangeReturnsTrue()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(pfRule50to150, pfRule100to200, false, false, true, true);
Assert.assertTrue(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesAreFirewallAndDuplicatedCidrsAndNoRangeConflictReturnsFalse()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(fwRule50to150, fwRule151to200, false, true, false, true);
Assert.assertFalse(result);
}
@Test
public void checkIfRulesHaveConflictingPortRangesTestBothRulesArePortForwardingAndDuplicatedCidrsAndNoRangeConflictReturnsFalse()
{
boolean result = _firewallMgr.checkIfRulesHaveConflictingPortRanges(pfRule50to150, pfRule151to200, false, false, true, true);
Assert.assertFalse(result);
}
}

View File

@ -83,6 +83,8 @@ import com.cloud.agent.api.GetRemoteVmsAnswer;
import com.cloud.agent.api.GetRemoteVmsCommand;
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
import com.cloud.agent.api.ImportConvertedInstanceAnswer;
import com.cloud.agent.api.ImportConvertedInstanceCommand;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.configuration.Resource;
import com.cloud.dc.ClusterVO;
@ -116,6 +118,7 @@ import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState;
import com.cloud.service.ServiceOfferingVO;
@ -371,9 +374,9 @@ public class UnmanagedVMsManagerImplTest {
doNothing().when(networkModel).checkNetworkPermissions(any(Account.class), any(Network.class));
NicProfile profile = Mockito.mock(NicProfile.class);
Integer deviceId = 100;
Pair<NicProfile, Integer> pair = new Pair<NicProfile, Integer>(profile, deviceId);
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), nullable(DataCenter.class), anyBoolean())).thenReturn(pair);
when(volumeDao.findByInstance(anyLong())).thenReturn(volumes);
Pair<NicProfile, Integer> pair = new Pair<>(profile, deviceId);
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), nullable(DataCenter.class), Mockito.anyBoolean())).thenReturn(pair);
when(volumeDao.findByInstance(Mockito.anyLong())).thenReturn(volumes);
List<UserVmResponse> userVmResponses = new ArrayList<>();
UserVmResponse userVmResponse = new UserVmResponse();
userVmResponse.setInstanceName(instance.getName());
@ -597,6 +600,7 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = mock(ClusterVO.class);
when(cluster.getId()).thenReturn(clusterId);
when(cluster.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(cluster.getDataCenterId()).thenReturn(zoneId);
when(clusterDao.findById(clusterId)).thenReturn(cluster);
@ -610,6 +614,7 @@ public class UnmanagedVMsManagerImplTest {
when(importVmCmd.getHostIp()).thenReturn(host);
when(importVmCmd.getNicNetworkList()).thenReturn(Map.of("NIC 1", networkId));
when(importVmCmd.getConvertInstanceHostId()).thenReturn(null);
when(importVmCmd.getImportInstanceHostId()).thenReturn(null);
when(importVmCmd.getConvertStoragePoolId()).thenReturn(null);
NetworkVO networkVO = Mockito.mock(NetworkVO.class);
@ -631,10 +636,14 @@ public class UnmanagedVMsManagerImplTest {
when(convertHost.getId()).thenReturn(convertHostId);
when(convertHost.getName()).thenReturn("KVM-Convert-Host");
when(convertHost.getType()).thenReturn(Host.Type.Routing);
when(convertHost.getDataCenterId()).thenReturn(zoneId);
when(convertHost.getClusterId()).thenReturn(clusterId);
if (selectConvertHost) {
when(importVmCmd.getConvertInstanceHostId()).thenReturn(convertHostId);
when(importVmCmd.getImportInstanceHostId()).thenReturn(convertHostId);
when(hostDao.findById(convertHostId)).thenReturn(convertHost);
} else {
when(hostDao.listByClusterAndHypervisorType(clusterId, Hypervisor.HypervisorType.KVM)).thenReturn(List.of(convertHost));
}
DataStoreTO dataStoreTO = mock(DataStoreTO.class);
@ -695,10 +704,14 @@ public class UnmanagedVMsManagerImplTest {
}
ConvertInstanceAnswer convertInstanceAnswer = mock(ConvertInstanceAnswer.class);
when(convertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
ImportConvertedInstanceAnswer convertImportedInstanceAnswer = mock(ImportConvertedInstanceAnswer.class);
when(convertInstanceAnswer.getConvertedInstance()).thenReturn(instance);
when(convertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
Mockito.lenient().when(convertImportedInstanceAnswer.getConvertedInstance()).thenReturn(instance);
Mockito.lenient().when(convertImportedInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) {
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ConvertInstanceCommand.class))).thenReturn(convertInstanceAnswer);
Mockito.lenient().when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ImportConvertedInstanceCommand.class))).thenReturn(convertImportedInstanceAnswer);
}
try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
@ -762,6 +775,7 @@ public class UnmanagedVMsManagerImplTest {
}
}
@Test
public void testImportVmFromVmwareToKvmExistingVcenter() throws OperationTimedoutException, AgentUnavailableException {
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, false, false);
}
@ -814,7 +828,7 @@ public class UnmanagedVMsManagerImplTest {
long poolId = 1L;
when(primaryDataStoreDao.findById(poolId)).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
}
@Test(expected = CloudRuntimeException.class)
@ -822,20 +836,35 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = getClusterForTests();
long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(100L);
when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
when(pool.getClusterId()).thenReturn(100L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationPoolConvertHostDifferentCluster() {
ClusterVO cluster = getClusterForTests();
long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class);
when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
when(pool.getClusterId()).thenReturn(1L);
HostVO host = mock(HostVO.class);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
when(host.getClusterId()).thenReturn(2L);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, host, poolId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationLocalStoragePoolInvalid() {
ClusterVO cluster = getClusterForTests();
long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST);
when(pool.getScope()).thenReturn(ScopeType.HOST);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
}
@Test(expected = CloudRuntimeException.class)
@ -843,18 +872,250 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = getClusterForTests();
long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(1L);
when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
when(pool.getClusterId()).thenReturn(1L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
ClusterVO cluster = getClusterForTests();
Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null);
when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, null);
}
@Test
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdSuccess() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Routing);
when(host.getClusterId()).thenReturn(1L);
when(hostDao.findById(hostId)).thenReturn(host);
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
Assert.assertEquals(host, returnedHost);
}
@Test
public void testSelectKVMHostForImportingInClusterWithNullImportInstanceIdSuccess() {
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(hostDao.listByClusterAndHypervisorType(cluster.getId(), cluster.getHypervisorType())).thenReturn(List.of(host));
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, null);
Assert.assertEquals(host, returnedHost);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterFailure() {
ClusterVO cluster = getClusterForTests();
when(hostDao.listByClusterAndHypervisorType(cluster.getId(), cluster.getHypervisorType())).thenReturn(List.of());
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, null);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdInvalidCluster() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Routing);
when(host.getClusterId()).thenReturn(2L);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdInvalidType() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Storage);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdInvalidStatus() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Alert);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdInvalidResourceState() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Disabled);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForImportingInClusterWithImportInstanceIdInvalidHostId() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
when(hostDao.findById(hostId)).thenReturn(null);
unmanagedVMsManager.selectKVMHostForImportingInCluster(cluster, hostId);
}
@Test
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdEnabledHost() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Routing);
when(host.getDataCenterId()).thenReturn(1L);
when(hostDao.findById(hostId)).thenReturn(host);
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
Assert.assertEquals(host, returnedHost);
}
@Test
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdDisabledHost() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Disabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Routing);
when(host.getDataCenterId()).thenReturn(1L);
when(hostDao.findById(hostId)).thenReturn(host);
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
Assert.assertEquals(host, returnedHost);
}
@Test
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdSuccessCompatible() {
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(hostDao.listByClusterHypervisorTypeAndHostCapability(cluster.getId(),
cluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION)).thenReturn(List.of(host));
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, null);
Assert.assertEquals(host, returnedHost);
}
@Test
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdSuccessNonCompatible() {
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(hostDao.listByClusterHypervisorTypeAndHostCapability(cluster.getId(),
cluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION)).thenReturn(List.of());
when(hostDao.listByClusterAndHypervisorType(cluster.getId(), cluster.getHypervisorType())).thenReturn(List.of(host));
HostVO returnedHost = unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, null);
Assert.assertEquals(host, returnedHost);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdFailure() {
ClusterVO cluster = getClusterForTests();
when(hostDao.listByClusterHypervisorTypeAndHostCapability(cluster.getId(),
cluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION)).thenReturn(List.of());
when(hostDao.listByClusterAndHypervisorType(cluster.getId(), cluster.getHypervisorType())).thenReturn(List.of());
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, null);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdInvalidZone() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.Routing);
when(host.getDataCenterId()).thenReturn(2L);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdInvalidType() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Up);
when(host.getType()).thenReturn(Host.Type.SecondaryStorage);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdInvalidStatus() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Enabled);
when(host.getStatus()).thenReturn(Status.Down);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdInvalidResourceState() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
HostVO host = Mockito.mock(HostVO.class);
when(host.getResourceState()).thenReturn(ResourceState.Maintenance);
when(hostDao.findById(hostId)).thenReturn(host);
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectKVMHostForConversionInClusterWithImportInstanceIdInvalidHostId() {
Long hostId = 1L;
ClusterVO cluster = getClusterForTests();
when(hostDao.findById(hostId)).thenReturn(null);
unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId);
}
@Test

View File

@ -1467,7 +1467,10 @@ class CsForwardingRules(CsDataBag):
self.fw.append(["filter", "", fw7])
def forward_vpc(self, rule):
fw_prerout_rule = "-A PREROUTING -d %s/32 " % (rule["public_ip"])
fw_prerout_rule = "-A PREROUTING"
if "source_cidr_list" in rule and rule["source_cidr_list"]:
fw_prerout_rule += " -s %s" % rule["source_cidr_list"]
fw_prerout_rule += " -d %s/32" % rule["public_ip"]
if not rule["protocol"] == "any":
fw_prerout_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"])
if not rule["public_ports"] == "any":
@ -1476,7 +1479,10 @@ class CsForwardingRules(CsDataBag):
if not rule["internal_ports"] == "any":
fw_prerout_rule += ":" + self.portsToString(rule["internal_ports"], "-")
fw_output_rule = "-A OUTPUT -d %s/32" % rule["public_ip"]
fw_output_rule = "-A OUTPUT"
if "source_cidr_list" in rule and rule["source_cidr_list"]:
fw_output_rule += " -s %s" % rule["source_cidr_list"]
fw_output_rule += " -d %s/32" % rule["public_ip"]
if not rule["protocol"] == "any":
fw_output_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"])
if not rule["public_ports"] == "any":

View File

@ -379,7 +379,7 @@ class CsNetfilter(object):
return self.rule
def to_str(self, delete=False):
""" Convert the rule back into aynactically correct iptables command """
""" Convert the rule back into syntactically correct iptables command """
# Order is important
order = ['-A', '-s', '-d', '!_-d', '-i', '!_-i', '-p', '-m', '-m2', '--icmp-type', '--state',
'--dport', '--destination-port', '-o', '!_-o', '-j', '--set-xmark', '--checksum',

View File

@ -16,6 +16,8 @@
# under the License.
import logging
def merge(dbag, rules):
for rule in rules["rules"]:
source_ip = rule["source_ip_address"]
@ -33,6 +35,8 @@ def merge(dbag, rules):
newrule["public_ports"] = rule["source_port_range"]
newrule["internal_ports"] = rule["destination_port_range"]
newrule["protocol"] = rule["protocol"]
if "source_cidr_list" in rule:
newrule["source_cidr_list"] = rule["source_cidr_list"]
if not revoke:
if rules["type"] == "staticnatrules":
@ -59,7 +63,7 @@ def merge(dbag, rules):
for forward in dbag[source_ip]:
if ruleCompare(forward, newrule):
index = dbag[source_ip].index(forward)
print("removing index %s" % str(index))
logging.info("Removing forwarding rule [%s] at index [%s].", forward, index)
if not index == -1:
del dbag[source_ip][index]
@ -74,4 +78,18 @@ def ruleCompare(ruleA, ruleB):
return ruleA["public_ip"] == ruleB["public_ip"]
elif ruleA["type"] == "forward":
return ruleA["public_ip"] == ruleB["public_ip"] and ruleA["public_ports"] == ruleB["public_ports"] \
and ruleA["protocol"] == ruleB["protocol"]
and ruleA["protocol"] == ruleB["protocol"] and cidrsConflict(ruleA.get("source_cidr_list"), ruleB.get("source_cidr_list"))
# Same validation as in com.cloud.network.firewall.FirewallManagerImpl.detectConflictingCidrs
def cidrsConflict(cidrListA, cidrListB):
if not cidrListA and not cidrListB:
return True
if not cidrListA:
return False
if not cidrListB:
return False
cidrListA = set(cidrListA.split(","))
cidrListB = set(cidrListB.split(","))
return len(cidrListA.intersection(cidrListB)) > 0

View File

@ -1000,7 +1000,7 @@
"label.for": "for",
"label.forbidden": "Forbidden",
"label.forced": "Force",
"label.force.ms.to.import.vm.files": "Force MS to import VM file(s) to temporary storage",
"label.force.ms.to.import.vm.files": "Force MS to export OVF from VMware to temporary storage",
"label.force.stop": "Force stop",
"label.force.reboot": "Force reboot",
"label.forceencap": "Force UDP encapsulation of ESP packets",
@ -1564,6 +1564,7 @@
"label.noselect": "No thanks",
"label.not.found": "Not found",
"label.not.suitable": "Not suitable",
"label.not.supported": "Not supported",
"label.notifications": "Notifications",
"label.nsx": "NSX",
"label.nsx.provider": "NSX Provider",
@ -2102,6 +2103,7 @@
"label.source": "Select Import-Export Source Hypervisor",
"label.source.based": "SourceBased",
"label.sourcecidr": "Source CIDR",
"label.sourcecidrlist": "Source CIDR list",
"label.sourcehost": "Source host",
"label.sourceipaddress": "Source IP address",
"label.sourceipaddressnetworkid": "Network ID of source IP address",
@ -3399,8 +3401,9 @@
"message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
"message.select.disk.offering": "Please select a disk offering for disk",
"message.select.end.date.and.time": "Select an end date & time.",
"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the cluster to perform the instance conversion through virt-v2v",
"message.select.load.balancer.rule": "Please select a load balancer rule for your AutoScale Group.",
"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the zone to perform the instance conversion through virt-v2v",
"message.select.kvm.host.instance.import": "(Optional) Select a KVM host in the cluster to perform the importing of the converted instance",
"message.select.load.balancer.rule": "Please select a load balancer rule for your AutoScale Instance group.",
"message.select.migration.policy": "Please select a migration policy.",
"message.select.nic.network": "Please select a Network for NIC",
"message.select.security.groups": "Please select security group(s) for your new Instance.",

View File

@ -352,7 +352,7 @@
"label.choose.saml.identity": "Escolha um provedor de identidade SAML",
"label.cidr": "CIDR",
"label.cidr.destination.network": "CIDR da rede de destino",
"label.cidrlist": "Lista de CIDR ",
"label.cidrlist": "Lista de CIDRs",
"label.cisco.nexus1000v.ip.address": "Endere\u00e7o IP do Nexus 1000v",
"label.cisco.nexus1000v.password": "Senha do Nexus 1000v",
"label.cisco.nexus1000v.username": "Usu\u00e1rio do Nexus 1000v",
@ -1510,6 +1510,7 @@
"label.sockettimeout": "Tempo limite no socket",
"label.source.based": "SourceBased",
"label.sourcecidr": "CIDR de origem",
"label.sourcecidrlist": "Lista de CIDRs de origem",
"label.sourceipaddress": "Endere\u00e7o IP de origem",
"label.sourcenat": "Source NAT",
"label.sourcenatsupported": "Suporte \u00e0 source NAT",

View File

@ -186,7 +186,14 @@
<div class="resource-detail-item__label">{{ $t('label.cpu') }}</div>
<div class="resource-detail-item__details">
<appstore-outlined />
<span v-if="'cpunumber' in resource && 'cpuspeed' in resource">{{ resource.cpunumber }} CPU x {{ parseFloat(resource.cpuspeed / 1000.0).toFixed(2) }} Ghz</span>
<span v-if="'cpunumber' in resource && 'cpuspeed' in resource">{{ resource.cpunumber }} CPU x {{ (resource.cpuspeed / 1000.0).toFixed(2) }} GHz
<a-tooltip placement="top">
<template #title>
{{ resource.cpuspeed }} MHz
</template>
<QuestionCircleOutlined />
</a-tooltip>
</span>
<span v-else>{{ resource.cputotal }}</span>
<a-tag v-if="resource.arch" style="margin-left: 10px">
{{ resource.arch }}
@ -233,7 +240,16 @@
<div class="resource-detail-item" v-else-if="resource.memorytotalgb">
<div class="resource-detail-item__label">{{ $t('label.memory') }}</div>
<div class="resource-detail-item__details">
<bulb-outlined />{{ resource.memorytotalgb + ' ' + $t('label.memory') }}
<bulb-outlined />
<span>
{{ resource.memorytotalgb + ' ' + $t('label.memory') }}
<a-tooltip placement="top">
<template #title>
{{ (resource.memorytotal/(1024**2)).toFixed(3) }} MB
</template>
<QuestionCircleOutlined />
</a-tooltip>
</span>
</div>
<div>
<span v-if="resource.memoryusedgb">

View File

@ -299,7 +299,6 @@ export default {
if (response) {
const oauthproviders = response.listoauthproviderresponse.oauthprovider || []
oauthproviders.forEach(item => {
this.socialLogin = true
if (item.provider === 'google') {
this.googleprovider = item.enabled
this.googleclientid = item.clientid
@ -311,6 +310,7 @@ export default {
this.githubredirecturi = item.redirecturi
}
})
this.socialLogin = this.googleprovider || this.githubprovider
}
})
api('forgotPassword', {}).then(response => {

View File

@ -37,7 +37,7 @@
</div>
<div class="form">
<div class="form__item" ref="newCidrList">
<tooltip-label :title="$t('label.cidrlist')" bold :tooltip="createLoadBalancerRuleParams.cidrlist.description" :tooltip-placement="'right'"/>
<tooltip-label :title="$t('label.sourcecidrlist')" bold :tooltip="createLoadBalancerRuleParams.cidrlist.description" :tooltip-placement="'right'"/>
<a-input v-model:value="newRule.cidrlist"></a-input>
</div>
<div class="form__item">
@ -126,7 +126,7 @@
:rowKey="record => record.id">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'cidrlist'">
<span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
<span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(",", "\n") }}</span>
</template>
<template v-if="column.key === 'algorithm'">
{{ returnAlgorithmName(record.algorithm) }}
@ -808,7 +808,7 @@ export default {
},
{
key: 'cidrlist',
title: this.$t('label.cidrlist')
title: this.$t('label.sourcecidrlist')
},
{
key: 'protocol',

View File

@ -72,6 +72,12 @@
<a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
</a-select>
</div>
<div v-if="isVPC()">
<div class="form__item" ref="newCidrList">
<tooltip-label :title="$t('label.sourcecidrlist')" bold :tooltip="apiParams.cidrlist.description" :tooltip-placement="'right'"/>
<a-input v-model:value="newRule.cidrlist"></a-input>
</div>
</div>
<div class="form__item" style="margin-left: auto;">
<div class="form__label">{{ $t('label.add.vm') }}</div>
<a-button :disabled="!('createPortForwardingRule' in $store.getters.apis)" type="primary" @click="openAddVMModal">{{ $t('label.add') }}</a-button>
@ -108,6 +114,9 @@
<template v-if="column.key === 'protocol'">
{{ getCapitalise(record.protocol) }}
</template>
<template v-if="column.key === 'cidrlist'">
<span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(",", "\n") }}</span>
</template>
<template v-if="column.key === 'vm'">
<div><desktop-outlined/>
<router-link
@ -334,9 +343,11 @@ import Status from '@/components/widgets/Status'
import TooltipButton from '@/components/widgets/TooltipButton'
import BulkActionView from '@/components/view/BulkActionView'
import eventBus from '@/config/eventBus'
import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
export default {
components: {
TooltipLabel,
Status,
TooltipButton,
BulkActionView
@ -399,6 +410,11 @@ export default {
key: 'protocol',
title: this.$t('label.protocol')
},
{
key: 'cidrlist',
title: this.$t('label.sourcecidrlist'),
hidden: !this.isVPC()
},
{
title: this.$t('label.state'),
dataIndex: 'state'
@ -411,7 +427,7 @@ export default {
key: 'actions',
title: this.$t('label.actions')
}
],
].filter(item => !item.hidden),
tiers: {
loading: false,
data: []
@ -450,7 +466,8 @@ export default {
vmPage: 1,
vmPageSize: 10,
vmCount: 0,
searchQuery: null
searchQuery: null,
cidrlist: ''
}
},
computed: {
@ -458,6 +475,9 @@ export default {
return this.selectedRowKeys.length > 0
}
},
beforeCreate () {
this.apiParams = this.$getApiParams('createPortForwardingRule')
},
created () {
console.log(this.resource)
this.initForm()
@ -830,6 +850,9 @@ export default {
onSearch (value) {
this.searchQuery = value
this.fetchVirtualMachines()
},
isVPC () {
return 'vpcid' in this.resource
}
}
}

View File

@ -31,27 +31,23 @@
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.addAccountToProject.account.description"/>
</template>
<a-select
show-search
<a-auto-complete
v-model:value="form.account"
:placeholder="apiParams.addAccountToProject.account.description"
v-focus="true"
:filterOption="false"
@search="fetchAccounts"
>
<template v-if="load.accounts" #notFoundContent>
<a-spin size="small" />
</template>
<template v-if="!load.accounts">
<a-select-option v-for="account in accounts" :key="account.name" :value="account.name">
<span v-if="account.icon">
<resource-icon :image="account.icon.base64image" size="1x" style="margin-right: 5px"/>
</span>
<block-outlined v-else style="margin-right: 5px" />
{{ account.name }}
</a-select-option>
</template>
</a-select>
:filterOption="filterOption"
:options="accounts"
>
<template v-if="load.accounts" #notFoundContent>
<a-spin size="small" />
</template>
<template v-if="!load.accounts" #option="account">
<span v-if="account.icon">
<resource-icon :image="account.icon.base64image" size="1x" style="margin-right: 5px"/>
</span>
<block-outlined v-else style="margin-right: 5px" />
{{ account.name }}
</template>
</a-auto-complete>
</a-form-item>
<a-form-item name="email" ref="email">
<template #label>
@ -121,27 +117,23 @@
<template #label>
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.addUserToProject.username.description"/>
</template>
<a-select
show-search
<a-auto-complete
v-model:value="form.username"
:placeholder="apiParams.addUserToProject.username.description"
v-focus="true"
:filterOption="false"
@search="fetchUsers"
:filterOption="filterOption"
:options="users"
>
<template v-if="load.users" #notFoundContent>
<a-spin size="small" />
</template>
<template v-if="!load.users">
<a-select-option v-for="user in users" :key="user.username" :value="user.username">
<span v-if="user.icon">
<resource-icon :image="user.icon.base64image" size="1x" style="margin-right: 5px"/>
</span>
<block-outlined v-else style="margin-right: 5px" />
{{ user.firstname + ' ' + user.lastname + " (" + user.username + ")" }}
</a-select-option>
<template v-if="!load.users" #option="user">
<span v-if="user.icon">
<resource-icon :image="user.icon.base64image" size="1x" style="margin-right: 5px"/>
</span>
<block-outlined v-else style="margin-right: 5px" />
{{ user.firstName + ' ' + user.lastName + " (" + user.username + ")" }}
</template>
</a-select>
</a-auto-complete>
</a-form-item>
<a-form-item name="email" ref="email">
<template #label>
@ -254,6 +246,11 @@ export default {
this.fetchProjectRoles()
}
},
filterOption (input, option) {
return (
option.value.toUpperCase().indexOf(input.toUpperCase()) >= 0
)
},
fetchUsers (keyword) {
this.load.users = true
const params = { listall: true, showicon: true }
@ -261,13 +258,28 @@ export default {
params.keyword = keyword
}
api('listUsers', params).then(response => {
this.users = response.listusersresponse.user ? response.listusersresponse.user : []
this.users = this.parseUsers(response?.listusersresponse?.user)
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.load.users = false
})
},
parseUsers (users) {
if (!users) {
return []
}
return users.map(user => {
return {
value: user.username,
username: user.username,
firstName: user.firstname,
lastName: user.lastname,
icon: user.icon
}
})
},
fetchAccounts (keyword) {
this.load.accounts = true
const params = { domainid: this.resource.domainid, showicon: true }
@ -275,13 +287,26 @@ export default {
params.keyword = keyword
}
api('listAccounts', params).then(response => {
this.accounts = response.listaccountsresponse.account || []
this.accounts = this.parseAccounts(response?.listaccountsresponse?.account)
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.load.accounts = false
})
},
parseAccounts (accounts) {
if (!accounts) {
return []
}
return accounts.map(account => {
return {
value: account.name,
name: account.name,
icon: account.icon
}
})
},
fetchProjectRoles () {
this.load.projectRoles = true
api('listProjectRoles', {

View File

@ -164,6 +164,18 @@
@handle-checkselectpair-change="updateSelectedKvmHostForConversion"
/>
</a-form-item>
<a-form-item name="importhostid" ref="importhostid">
<check-box-select-pair
layout="vertical"
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
:resourceKey="cluster.id"
:selectOptions="kvmHostsForImporting"
:checkBoxLabel="$t('message.select.kvm.host.instance.import')"
:defaultCheckBoxValue="false"
:reversed="false"
@handle-checkselectpair-change="updateSelectedKvmHostForImporting"
/>
</a-form-item>
<a-form-item name="convertstorageoption" ref="convertstorageoption">
<check-box-select-pair
layout="vertical"
@ -494,7 +506,9 @@ export default {
switches: {},
loading: false,
kvmHostsForConversion: [],
kvmHostsForImporting: [],
selectedKvmHostForConversion: null,
selectedKvmHostForImporting: null,
storageOptionsForConversion: [
{
id: 'secondary',
@ -730,6 +744,7 @@ export default {
page: 1
})
this.fetchKvmHostsForConversion()
this.fetchKvmHostsForImporting()
if (this.resource?.disk?.length > 1) {
this.updateSelectedRootDisk()
}
@ -914,6 +929,25 @@ export default {
}
},
fetchKvmHostsForConversion () {
api('listHosts', {
zoneid: this.zoneid,
hypervisor: this.cluster.hypervisortype,
type: 'Routing',
state: 'Up'
}).then(json => {
this.kvmHostsForConversion = json.listhostsresponse.host || []
this.kvmHostsForConversion = this.kvmHostsForConversion.filter(host => ['Enabled', 'Disabled'].includes(host.resourcestate))
this.kvmHostsForConversion.map(host => {
host.name = host.name + ' [Pod=' + host.podname + '] [Cluster=' + host.clustername + ']'
if (host.instanceconversionsupported !== null && host.instanceconversionsupported !== undefined && host.instanceconversionsupported) {
host.name = host.name + ' (' + this.$t('label.supported') + ')'
} else {
host.name = host.name + ' (' + this.$t('label.not.supported') + ')'
}
})
})
},
fetchKvmHostsForImporting () {
api('listHosts', {
clusterid: this.cluster.id,
hypervisor: this.cluster.hypervisortype,
@ -921,20 +955,22 @@ export default {
state: 'Up',
resourcestate: 'Enabled'
}).then(json => {
this.kvmHostsForConversion = json.listhostsresponse.host || []
this.kvmHostsForConversion.map(host => {
if (host.instanceconversionsupported !== null && host.instanceconversionsupported !== undefined && host.instanceconversionsupported) {
host.name = host.name + ' (' + this.$t('label.supported') + ')'
}
})
this.kvmHostsForImporting = json.listhostsresponse.host || []
})
},
fetchStoragePoolsForConversion () {
if (this.selectedStorageOptionForConversion === 'primary') {
api('listStoragePools', {
zoneid: this.cluster.zoneid,
const params = {
clusterid: this.cluster.id,
status: 'Up'
}).then(json => {
}
if (this.selectedKvmHostForConversion) {
const kvmHost = this.kvmHostsForConversion.filter(x => x.id === this.selectedKvmHostForConversion)[0]
if (kvmHost.clusterid !== this.cluster.id) {
params.scope = 'ZONE'
}
}
api('listStoragePools', params).then(json => {
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
})
} else if (this.selectedStorageOptionForConversion === 'local') {
@ -948,6 +984,14 @@ export default {
})
}
},
updateSelectedKvmHostForImporting (clusterid, checked, value) {
if (checked) {
this.selectedKvmHostForImporting = value
} else {
this.selectedKvmHostForImporting = null
this.resetStorageOptionsForConversion()
}
},
updateSelectedKvmHostForConversion (clusterid, checked, value) {
if (checked) {
this.selectedKvmHostForConversion = value
@ -1099,6 +1143,9 @@ export default {
if (this.selectedKvmHostForConversion) {
params.convertinstancehostid = this.selectedKvmHostForConversion
}
if (this.selectedKvmHostForImporting) {
params.importinstancehostid = this.selectedKvmHostForImporting
}
if (this.selectedStoragePoolForConversion) {
params.convertinstancepoolid = this.selectedStoragePoolForConversion
}

View File

@ -840,7 +840,8 @@ export default {
options: {
zoneid: _.get(this.zone, 'id'),
podid: this.podId,
hypervisor: this.destinationHypervisor
hypervisor: this.destinationHypervisor,
allocationstate: 'Enabled'
},
field: 'clusterid'
},