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.FirewallRule;
import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.PortForwardingRule;
import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.NetUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/** /**
* PortForwardingRuleTO specifies one port forwarding rule. * PortForwardingRuleTO specifies one port forwarding rule.
@ -29,6 +32,8 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
String dstIp; String dstIp;
int[] dstPortRange; int[] dstPortRange;
List<String> sourceCidrList;
protected PortForwardingRuleTO() { protected PortForwardingRuleTO() {
super(); super();
} }
@ -37,6 +42,7 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
super(rule, srcVlanTag, srcIp); super(rule, srcVlanTag, srcIp);
this.dstIp = rule.getDestinationIpAddress().addr(); this.dstIp = rule.getDestinationIpAddress().addr();
this.dstPortRange = new int[] {rule.getDestinationPortStart(), rule.getDestinationPortEnd()}; 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, 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); 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.user.Account;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.net.Ip; import com.cloud.utils.net.Ip;
import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd;
public interface RulesService { public interface RulesService {
Pair<List<? extends FirewallRule>, Integer> searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId, 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; 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 ICMP_TYPE = "icmptype";
public static final String ID = "id"; public static final String ID = "id";
public static final String IDS = "ids"; 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 INDEX = "index";
public static final String INSTANCES_DISKS_STATS_RETENTION_ENABLED = "instancesdisksstatsretentionenabled"; public static final String INSTANCES_DISKS_STATS_RETENTION_ENABLED = "instancesdisksstatsretentionenabled";
public static final String INSTANCES_DISKS_STATS_RETENTION_TIME = "instancesdisksstatsretentiontime"; 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_POLICY_ID = "snapshotpolicyid";
public static final String SNAPSHOT_TYPE = "snapshottype"; public static final String SNAPSHOT_TYPE = "snapshottype";
public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; 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 SOURCE_ZONE_ID = "sourcezoneid";
public static final String SSL_VERIFICATION = "sslverification"; public static final String SSL_VERIFICATION = "sslverification";
public static final String START_ASN = "startasn"; public static final String START_ASN = "startasn";

View File

@ -144,15 +144,19 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
private String clusterName; private String clusterName;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, @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; 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, @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.") 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; private Long convertStoragePoolId;
@Parameter(name = ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, type = CommandType.BOOLEAN, @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; private Boolean forceMsToImportVmFiles;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -199,6 +203,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
return convertInstanceHostId; return convertInstanceHostId;
} }
public Long getImportInstanceHostId() {
return importInstanceHostId;
}
public Long getConvertStoragePoolId() { public Long getConvertStoragePoolId() {
return convertStoragePoolId; 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") description = "the ID of the virtual machine for the port forwarding rule")
private Long virtualMachineId; 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.") @Parameter(name = ApiConstants.CIDR_LIST,
private List<String> cidrlist; 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; " @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" + "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 @Override
public List<String> getSourceCidrList() { public List<String> getSourceCidrList() {
if (cidrlist != null) { return sourceCidrList;
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;
} }
public Boolean getOpenFirewall() { public Boolean getOpenFirewall() {
@ -332,12 +332,6 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
@Override @Override
public void create() { 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(); Ip privateIp = getVmSecondaryIp();
if (privateIp != null) { if (privateIp != null) {
if (!NetUtils.isValidIp4(privateIp.toString())) { if (!NetUtils.isValidIp4(privateIp.toString())) {
@ -345,6 +339,8 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
} }
} }
_rulesService.validatePortForwardingSourceCidrList(sourceCidrList);
try { try {
PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall(), isDisplay()); PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall(), isDisplay());
setEntityId(result.getId()); setEntityId(result.getId());

View File

@ -32,6 +32,8 @@ import com.cloud.network.rules.PortForwardingRule;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.utils.net.Ip; import com.cloud.utils.net.Ip;
import java.util.List;
@APICommand(name = "updatePortForwardingRule", @APICommand(name = "updatePortForwardingRule",
responseObject = FirewallRuleResponse.class, responseObject = FirewallRuleResponse.class,
description = "Updates a port forwarding rule. Only the private port and the virtual machine can be updated.", entityType = {PortForwardingRule.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}) @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; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -94,6 +103,10 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd {
return display; return display;
} }
public List<String> getSourceCidrList() {
return sourceCidrList;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -130,7 +143,7 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd {
@Override @Override
public void execute() { public void execute() {
PortForwardingRule rule = _rulesService.updatePortForwardingRule(getId(), getPrivatePort(), getPrivateEndPort(), getVirtualMachineId(), getVmGuestIp(), getCustomId(), getDisplay()); PortForwardingRule rule = _rulesService.updatePortForwardingRule(this);
FirewallRuleResponse fwResponse = new FirewallRuleResponse(); FirewallRuleResponse fwResponse = new FirewallRuleResponse();
if (rule != null) { if (rule != null) {
fwResponse = _responseGenerator.createPortForwardingRuleResponse(rule); 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") @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the load balancer")
private Long domainId; 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.") + "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; private List<String> cidrlist;

View File

@ -20,6 +20,8 @@ import org.apache.cloudstack.vm.UnmanagedInstanceTO;
public class ConvertInstanceAnswer extends Answer { public class ConvertInstanceAnswer extends Answer {
private String temporaryConvertUuid;
public ConvertInstanceAnswer() { public ConvertInstanceAnswer() {
super(); super();
} }
@ -34,6 +36,15 @@ public class ConvertInstanceAnswer extends Answer {
this.convertedInstance = convertedInstance; this.convertedInstance = convertedInstance;
} }
public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) {
super(command, true, "");
this.temporaryConvertUuid = temporaryConvertUuid;
}
public String getTemporaryConvertUuid() {
return temporaryConvertUuid;
}
public UnmanagedInstanceTO getConvertedInstance() { public UnmanagedInstanceTO getConvertedInstance() {
return convertedInstance; 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()) { for (final PortForwardingRuleTO rule : command.getRules()) {
final ForwardingRule fwdRule = new ForwardingRule(rule.revoked(), rule.getProtocol().toLowerCase(), rule.getSrcIp(), rule.getStringSrcPortRange(), rule.getDstIp(), 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); rules.add(fwdRule);
} }

View File

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

View File

@ -29,4 +29,6 @@ public interface FirewallRulesCidrsDao extends GenericDao<FirewallRulesCidrsVO,
@DB @DB
List<FirewallRulesCidrsVO> listByFirewallRuleId(long firewallRuleId); 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 java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
@ -45,7 +46,7 @@ public class FirewallRulesCidrsDaoImpl extends GenericDaoBase<FirewallRulesCidrs
sc.setParameters("firewallRuleId", firewallRuleId); sc.setParameters("firewallRuleId", firewallRuleId);
List<FirewallRulesCidrsVO> results = search(sc, null); 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) { for (FirewallRulesCidrsVO result : results) {
cidrs.add(result.getCidr()); cidrs.add(result.getCidr());
} }
@ -63,9 +64,27 @@ public class FirewallRulesCidrsDaoImpl extends GenericDaoBase<FirewallRulesCidrs
return results; 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 @Override
@DB @DB
public void persist(long firewallRuleId, List<String> sourceCidrs) { public void persist(long firewallRuleId, List<String> sourceCidrs) {
if (CollectionUtils.isEmpty(sourceCidrs)) {
return;
}
TransactionLegacy txn = TransactionLegacy.currentTxn(); TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start(); txn.start();

View File

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

View File

@ -25,6 +25,7 @@ import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient;
import com.cloud.utils.net.Ip; import com.cloud.utils.net.Ip;
@ -47,21 +48,30 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi
@Column(name = "instance_id") @Column(name = "instance_id")
private long virtualMachineId; private long virtualMachineId;
@Transient
List<String> sourceCidrs;
public PortForwardingRuleVO() { public PortForwardingRuleVO() {
} }
public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId, 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) { long accountId, long domainId, long instanceId, List<String> sourceCidrs) {
super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, null, null, null, null, null); super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, sourceCidrs, null, null, null, null);
this.destinationIpAddress = dstIp; this.destinationIpAddress = dstIp;
this.virtualMachineId = instanceId; this.virtualMachineId = instanceId;
this.destinationPortStart = dstPortStart; this.destinationPortStart = dstPortStart;
this.destinationPortEnd = dstPortEnd; 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, public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId,
long domainId, long instanceId) { long accountId, long domainId, long instanceId) {
this(xId, srcIpId, srcPort, srcPort, dstIp, dstPort, dstPort, protocol.toLowerCase(), networkId, accountId, domainId, 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 @Override
@ -106,4 +116,13 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi
return null; 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.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Op; 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 @Component
public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRuleVO, Long> implements PortForwardingRulesDao { public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRuleVO, Long> implements PortForwardingRulesDao {
@ -42,7 +45,7 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRul
protected final SearchBuilder<PortForwardingRuleVO> ActiveRulesSearchByAccount; protected final SearchBuilder<PortForwardingRuleVO> ActiveRulesSearchByAccount;
@Inject @Inject
protected FirewallRulesCidrsDao _portForwardingRulesCidrsDao; protected FirewallRulesCidrsDao portForwardingRulesCidrsDao;
protected PortForwardingRulesDaoImpl() { protected PortForwardingRulesDaoImpl() {
super(); super();
@ -183,4 +186,43 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase<PortForwardingRul
sc.setParameters("vmIds", vmIds.toArray()); sc.setParameters("vmIds", vmIds.toArray());
return batchExpunge(sc, batchSize); 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") @Column(name = "id")
long id; long id;
@Column(name = "last_id")
private long lastId;
@Column(name = "name") @Column(name = "name")
String name; String name;
@ -690,4 +693,12 @@ public class VolumeVO implements Volume {
public void setDeleteProtection(boolean deleteProtection) { public void setDeleteProtection(boolean deleteProtection) {
this.deleteProtection = 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 UPDATE `cloud`.`configuration` SET
`options` = 'FirstFitRouting,RandomAllocator,TestingAllocator,FirstFitAllocator,RecreateHostAllocator' `options` = 'FirstFitRouting,RandomAllocator,TestingAllocator,FirstFitAllocator,RecreateHostAllocator'
WHERE `name` = 'host.allocators.order'; 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) { if (success) {
VolumeVO volumeVO = _volumeDao.findById(destVolumeInfo.getId()); VolumeVO volumeVO = _volumeDao.findById(destVolumeInfo.getId());
volumeVO.setFormat(ImageFormat.QCOW2); volumeVO.setFormat(ImageFormat.QCOW2);
volumeVO.setLastId(srcVolumeInfo.getId());
_volumeDao.update(volumeVO.getId(), volumeVO); _volumeDao.update(volumeVO.getId(), volumeVO);
_volumeService.copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(Event.OperationSuccessed, null, srcVolumeInfo, destVolumeInfo, false); _volumeService.copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(Event.OperationSuccessed, null, srcVolumeInfo, destVolumeInfo, false);

View File

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

View File

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

View File

@ -127,11 +127,10 @@ public class LibvirtDomainXMLParser {
} }
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt); def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
} else if (device.equalsIgnoreCase("cdrom")) { } 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")) { } else if (type.equalsIgnoreCase("block")) {
def.defBlockBasedDisk(diskDev, diskLabel, parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
DiskDef.DiskBus.valueOf(bus.toUpperCase()));
} }
if (StringUtils.isNotBlank(diskCacheMode)) { if (StringUtils.isNotBlank(diskCacheMode)) {
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase())); def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@ -450,6 +449,25 @@ public class LibvirtDomainXMLParser {
return node.getAttribute(attr); 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() { public Integer getVncPort() {
return vncPort; return vncPort;
} }

View File

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

View File

@ -130,34 +130,25 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
final String temporaryConvertUuid = UUID.randomUUID().toString(); final String temporaryConvertUuid = UUID.randomUUID().toString();
boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
boolean cleanupSecondaryStorage = false;
try { try {
boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid, boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
timeout, verboseModeEnabled); timeout, verboseModeEnabled);
if (!result) { if (!result) {
String err = String.format("The virt-v2v conversion for the OVF %s failed. " + String err = String.format(
"Please check the agent logs for the virt-v2v output", ovfTemplateDirOnConversionLocation); "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); logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err); return new ConvertInstanceAnswer(cmd, false, err);
} }
String convertedBasePath = String.format("%s/%s", temporaryConvertPath, temporaryConvertUuid); return new ConvertInstanceAnswer(cmd, 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);
} catch (Exception e) { } catch (Exception e) {
String error = String.format("Error converting instance %s from %s, due to: %s", String error = String.format("Error converting instance %s from %s, due to: %s",
sourceInstanceName, sourceHypervisorType, e.getMessage()); sourceInstanceName, sourceHypervisorType, e.getMessage());
logger.error(error, e); logger.error(error, e);
cleanupSecondaryStorage = true;
return new ConvertInstanceAnswer(cmd, false, error); return new ConvertInstanceAnswer(cmd, false, error);
} finally { } finally {
if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) { if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
@ -165,7 +156,7 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir); logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir);
FileUtil.deletePath(sourceOVFDir); FileUtil.deletePath(sourceOVFDir);
} }
if (conversionTemporaryLocation instanceof NfsTO) { if (cleanupSecondaryStorage && conversionTemporaryLocation instanceof NfsTO) {
logger.debug("Cleaning up secondary storage temporary location"); logger.debug("Cleaning up secondary storage temporary location");
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid()); 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); storagePool = storagePoolMgr.getStoragePoolByURI(path);
} }
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name); final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
isoPath = isoVol.getPath(); isoPath = isoVol.getPath();
iso.defISODisk(isoPath, isUefiEnabled); iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
} else { } else {
iso.defISODisk(null, isUefiEnabled); iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
} }
final List<DiskDef> disks = resource.getDisks(conn, vmName); 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 * Checks if downloaded template is extractable
* @return true if it should be extracted, false if not * @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}'"); String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
} }
@ -183,7 +183,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* @param downloadedTemplateFile * @param downloadedTemplateFile
* @param templateUuid * @param templateUuid
*/ */
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) { public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
if (downloadedTemplateFile.endsWith(".zip")) { if (downloadedTemplateFile.endsWith(".zip")) {
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid; return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
} else if (downloadedTemplateFile.endsWith(".bz2")) { } else if (downloadedTemplateFile.endsWith(".bz2")) {
@ -198,7 +198,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
/** /**
* Extract downloaded template into installPath, remove compressed file * 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); String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript(extractCommand);
Script.runSimpleBashScript("rm -f " + downloadedTemplateFile); Script.runSimpleBashScript("rm -f " + downloadedTemplateFile);

View File

@ -18,7 +18,6 @@
// //
package com.cloud.hypervisor.kvm.resource.wrapper; package com.cloud.hypervisor.kvm.resource.wrapper;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -80,7 +79,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(storagePoolManager.getStoragePoolByURI(secondaryPoolUrl)).thenReturn(temporaryPool); Mockito.when(storagePoolManager.getStoragePoolByURI(secondaryPoolUrl)).thenReturn(temporaryPool);
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class); KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class); KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2));
} }
@Test @Test
@ -107,51 +105,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Assert.assertNotNull(temporaryStoragePool); 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 @Test
public void testSanitizeDisksPath() { public void testSanitizeDisksPath() {
String relativePath = UUID.randomUUID().toString(); 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, sourcePort, sourcePort,
vmIp, vmIp,
destPort, destPort, destPort, destPort,
"tcp", networkId, accountId, domainId, vmId); "tcp", networkId, accountId, domainId, vmId, null);
newRule.setDisplay(true); newRule.setDisplay(true);
newRule.setState(FirewallRule.State.Add); newRule.setState(FirewallRule.State.Add);
newRule = portForwardingRulesDao.persist(newRule); 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 - Disable discard="unmap" for ide devices and qemu < 7.0
https://bugzilla.redhat.com/show_bug.cgi?id=2029980 https://bugzilla.redhat.com/show_bug.cgi?id=2029980
## [2024-10-14]
### Added
- Support for ISO direct download to primary storage
## [2024-10-04] ## [2024-10-04]
### Added ### Added

View File

@ -23,11 +23,13 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import org.apache.cloudstack.storage.datastore.util.LinstorUtil; import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
import org.apache.cloudstack.utils.qemu.QemuImg; 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.Volume;
import com.linbit.linstor.api.model.VolumeDefinition; import com.linbit.linstor.api.model.VolumeDefinition;
import java.io.File;
public class LinstorStorageAdaptor implements StorageAdaptor { public class LinstorStorageAdaptor implements StorageAdaptor {
protected Logger logger = LogManager.getLogger(getClass()); protected Logger logger = LogManager.getLogger(getClass());
private static final Map<String, KVMStoragePool> MapStorageUuidToStoragePool = new HashMap<>(); 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); name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
final DevelopersApi api = getLinstorAPI(destPools); final DevelopersApi api = getLinstorAPI(destPools);
final String rscName = LinstorUtil.RSC_PREFIX + name; applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
try {
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
} catch (ApiException apiExc) {
logger.error("Error setting aux properties for {}", rscName);
logLinstorAnswers(apiExc.getApiCallRcList());
}
logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath()); logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath());
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath()); final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@ -623,13 +621,58 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
return null; 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 @Override
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
KVMStoragePool destPool, Storage.ImageFormat format, KVMStoragePool destPool, Storage.ImageFormat format,
int timeout) int timeout)
{ {
logger.debug("Linstor: createTemplateFromDirectDownloadFile"); 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) { public long getCapacity(LinstorStoragePool pool) {

View File

@ -30,6 +30,9 @@ import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.NsxProviderDao;
import com.cloud.network.element.NsxProviderVO; 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.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.springframework.stereotype.Component;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
import com.cloud.domain.dao.DomainDao; 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() ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol()
.equalsIgnoreCase(rule.getProtocol())))); .equalsIgnoreCase(rule.getProtocol()))));
// if both rules are firewall and their cidrs are different, we can skip port ranges verification // if both rules are firewall/port forwarding and their cidrs are different, we can skip port ranges verification
boolean bothRulesFirewall = (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall);
boolean duplicatedCidrs = false; boolean duplicatedCidrs = false;
boolean bothRulesFirewall = (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall);
if (bothRulesFirewall) { if (bothRulesFirewall) {
_firewallDao.loadSourceCidrs(rule); _firewallDao.loadSourceCidrs(rule);
_firewallDao.loadSourceCidrs((FirewallRuleVO)newRule); _firewallDao.loadSourceCidrs((FirewallRuleVO)newRule);
if (ObjectUtils.anyNull(rule.getSourceCidrList(), newRule.getSourceCidrList())) {
continue;
}
_firewallDao.loadDestinationCidrs(rule); _firewallDao.loadDestinationCidrs(rule);
_firewallDao.loadDestinationCidrs((FirewallRuleVO) newRule); _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; continue;
} }
duplicatedCidrs = (detectConflictingCidrs(rule.getSourceCidrList(), newRule.getSourceCidrList()) && detectConflictingCidrs(rule.getDestinationCidrList(), newRule.getDestinationCidrList()));
duplicatedCidrs = detectConflictingCidrs(rule.getSourceCidrList(), newRule.getSourceCidrList());
} }
if (!oneOfRulesIsFirewall) { if (!oneOfRulesIsFirewall) {
@ -451,18 +467,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
if (!notNullPorts) { if (!notNullPorts) {
continue; continue;
} else if (!oneOfRulesIsFirewall && } else if (checkIfRulesHaveConflictingPortRanges(newRule, rule, oneOfRulesIsFirewall, bothRulesFirewall, bothRulesPortForwarding, duplicatedCidrs)) {
!(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.
// we allow port forwarding rules with the same parameters but different protocols // we allow port forwarding rules with the same parameters but different protocols
boolean allowPf = boolean allowPf =
(rule.getPurpose() == Purpose.PortForwarding && newRule.getPurpose() == Purpose.PortForwarding && !newRule.getProtocol().equalsIgnoreCase( (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 @Override
public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type, public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type,
Long networkId, FirewallRule.TrafficType trafficType) { Long networkId, FirewallRule.TrafficType trafficType) {

View File

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

View File

@ -33,6 +33,7 @@ import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VirtualMachineProfileImpl; import com.cloud.vm.VirtualMachineProfileImpl;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.command.user.firewall.ListPortForwardingRulesCmd; 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.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; 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.NicSecondaryIpVO;
import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDao;
import org.apache.commons.collections.CollectionUtils;
public class RulesManagerImpl extends ManagerBase implements RulesManager, RulesService { public class RulesManagerImpl extends ManagerBase implements RulesManager, RulesService {
@ -112,7 +114,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
@Inject @Inject
PortForwardingRulesDao _portForwardingDao; PortForwardingRulesDao _portForwardingDao;
@Inject @Inject
FirewallRulesCidrsDao _firewallCidrsDao; FirewallRulesCidrsDao firewallCidrsDao;
@Inject @Inject
FirewallRulesDao _firewallDao; FirewallRulesDao _firewallDao;
@Inject @Inject
@ -247,6 +249,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
final Long accountId = ipAddress.getAllocatedToAccountId(); final Long accountId = ipAddress.getAllocatedToAccountId();
final Long domainId = ipAddress.getAllocatedInDomainId(); final Long domainId = ipAddress.getAllocatedInDomainId();
List<String> sourceCidrList = rule.getSourceCidrList();
// start port can't be bigger than end port // start port can't be bigger than end port
if (rule.getDestinationPortStart() > rule.getDestinationPortEnd()) { if (rule.getDestinationPortStart() > rule.getDestinationPortEnd()) {
@ -310,9 +313,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
final IPAddressVO ipAddressFinal = ipAddress; final IPAddressVO ipAddressFinal = ipAddress;
return Transaction.execute((TransactionCallbackWithException<PortForwardingRuleVO, NetworkRuleConflictException>) status -> { return Transaction.execute((TransactionCallbackWithException<PortForwardingRuleVO, NetworkRuleConflictException>) status -> {
PortForwardingRuleVO newRule = PortForwardingRuleVO newRule =
new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), dstIpFinal, new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), dstIpFinal,
rule.getDestinationPortStart(), rule.getDestinationPortEnd(), rule.getProtocol().toLowerCase(), networkId, accountId, domainId, vmId); rule.getDestinationPortStart(), rule.getDestinationPortEnd(), rule.getProtocol().toLowerCase(), networkId, accountId, domainId, vmId, sourceCidrList);
if (forDisplay != null) { if (forDisplay != null) {
newRule.setDisplay(forDisplay); 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()])); _accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()]));
} }
for (PortForwardingRuleVO rule : rules) {
rule.setSourceCidrList(firewallCidrsDao.getSourceCidrs(rule.getId()));
}
try { try {
if (!_firewallMgr.applyRules(rules, continueOnError, true)) { if (!_firewallMgr.applyRules(rules, continueOnError, true)) {
return false; 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()])); _accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()]));
} }
for (PortForwardingRuleVO rule: rules) {
rule.setSourceCidrList(firewallCidrsDao.getSourceCidrs(rule.getId()));
}
try { try {
if (!_firewallMgr.applyRules(rules, continueOnError, true)) { if (!_firewallMgr.applyRules(rules, continueOnError, true)) {
return false; return false;
@ -1568,12 +1578,22 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
@Override @Override
@ActionEvent(eventType = EventTypes.EVENT_NET_RULE_MODIFY, eventDescription = "updating forwarding rule", async = true) @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) { public PortForwardingRule updatePortForwardingRule(UpdatePortForwardingRuleCmd cmd) {
Account caller = CallContext.current().getCallingAccount(); 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); PortForwardingRuleVO rule = _portForwardingDao.findById(id);
if (rule == null) { if (rule == null) {
throw new InvalidParameterValueException("Unable to find " + id); throw new InvalidParameterValueException("Unable to find " + id);
} }
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, rule); _accountMgr.checkAccess(caller, null, true, rule);
if (customId != null) { if (customId != null) {
@ -1634,6 +1654,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
} }
} }
validatePortForwardingSourceCidrList(sourceCidrList);
// revoke old rules at first // revoke old rules at first
List<PortForwardingRuleVO> rules = new ArrayList<PortForwardingRuleVO>(); List<PortForwardingRuleVO> rules = new ArrayList<PortForwardingRuleVO>();
rule.setState(State.Revoke); rule.setState(State.Revoke);
@ -1661,6 +1683,11 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
rule.setVirtualMachineId(virtualMachineId); rule.setVirtualMachineId(virtualMachineId);
rule.setDestinationIpAddress(dstIp); rule.setDestinationIpAddress(dstIp);
} }
if (sourceCidrList != null) {
rule.setSourceCidrList(sourceCidrList);
}
_portForwardingDao.update(id, rule); _portForwardingDao.update(id, rule);
//apply new rules //apply new rules
@ -1670,4 +1697,17 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
return _portForwardingDao.findById(id); 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.GetRemoteVmsCommand;
import com.cloud.agent.api.GetUnmanagedInstancesAnswer; import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
import com.cloud.agent.api.GetUnmanagedInstancesCommand; 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.PrepareUnmanageVMInstanceAnswer;
import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand; import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
@ -1338,6 +1340,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
Long clusterId = cmd.getClusterId(); Long clusterId = cmd.getClusterId();
Cluster cluster = basicAccessChecks(clusterId); 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 Account caller = CallContext.current().getCallingAccount();
final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId()); final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId());
final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId()); final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
@ -1601,6 +1607,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
String clusterName = cmd.getClusterName(); String clusterName = cmd.getClusterName();
String sourceHostName = cmd.getHostIp(); String sourceHostName = cmd.getHostIp();
Long convertInstanceHostId = cmd.getConvertInstanceHostId(); Long convertInstanceHostId = cmd.getConvertInstanceHostId();
Long importInstanceHostId = cmd.getImportInstanceHostId();
Long convertStoragePoolId = cmd.getConvertStoragePoolId(); Long convertStoragePoolId = cmd.getConvertStoragePoolId();
if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) { if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) {
@ -1630,12 +1637,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
DataStoreTO temporaryConvertLocation = null; DataStoreTO temporaryConvertLocation = null;
String ovfTemplateOnConvertLocation = null; String ovfTemplateOnConvertLocation = null;
try { try {
HostVO convertHost = selectInstanceConversionKVMHostInCluster(destinationCluster, convertInstanceHostId); HostVO convertHost = selectKVMHostForConversionInCluster(destinationCluster, convertInstanceHostId);
HostVO importHost = selectKVMHostForImportingInCluster(destinationCluster, importInstanceHostId);
CheckConvertInstanceAnswer conversionSupportAnswer = checkConversionSupportOnHost(convertHost, sourceVMName, false); CheckConvertInstanceAnswer conversionSupportAnswer = checkConversionSupportOnHost(convertHost, sourceVMName, false);
logger.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" + 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)); " from VMware to KVM ", convertHost.getId(), convertHost.getName(), sourceVMName));
temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId); temporaryConvertLocation = selectInstanceConversionTemporaryLocation(
destinationCluster, convertHost, convertStoragePoolId);
List<StoragePoolVO> convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster); List<StoragePoolVO> convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster);
long importStartTime = System.currentTimeMillis(); long importStartTime = System.currentTimeMillis();
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName); 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(); int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value();
ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads); clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads);
convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools, convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName,
sourceVMwareInstance, convertHost, importHost, convertStoragePools,
temporaryConvertLocation, ovfTemplateOnConvertLocation); temporaryConvertLocation, ovfTemplateOnConvertLocation);
} else { } else {
// Uses KVM Host for OVF export to temporary conversion location, through ovftool // 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); temporaryConvertLocation, vcenter, username, password, datacenterName);
} }
@ -1814,23 +1825,81 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return params; return params;
} }
private HostVO selectInstanceConversionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) { HostVO selectKVMHostForImportingInCluster(Cluster destinationCluster, Long importInstanceHostId) {
if (convertInstanceHostId != null) { if (importInstanceHostId != null) {
HostVO selectedHost = hostDao.findById(convertInstanceHostId); String err = null;
HostVO selectedHost = hostDao.findById(importInstanceHostId);
if (selectedHost == null) { if (selectedHost == null) {
String msg = String.format("Cannot find host with ID %s", convertInstanceHostId); err = String.format("Cannot find host with ID %s to import the instance",
logger.error(msg); importInstanceHostId);
throw new CloudRuntimeException(msg); } 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 || if (err != null) {
selectedHost.getClusterId() != destinationCluster.getId()) { logger.error(err);
String msg = String.format("Cannot perform the conversion on the host %s as it is not a running and Enabled host", selectedHost.getName()); throw new CloudRuntimeException(err);
logger.error(msg);
throw new CloudRuntimeException(msg);
} }
return selectedHost; 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 // Auto select host with conversion capability
List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION); List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION);
if (CollectionUtils.isNotEmpty(hosts)) { if (CollectionUtils.isNotEmpty(hosts)) {
@ -1875,10 +1944,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return checkConvertInstanceAnswer; return checkConvertInstanceAnswer;
} }
private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost, private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation, String sourceVM, UnmanagedInstanceTO sourceVMwareInstance,
String ovfTemplateDirConvertLocation) { HostVO convertHost, HostVO importHost,
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", 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)); sourceVM, convertHost.getId(), convertHost.getName(), ovfTemplateDirConvertLocation));
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM); RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM);
@ -1907,9 +1979,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance(); return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
} }
private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost, private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation, String sourceVM, UnmanagedInstanceTO sourceVMwareInstance,
String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { 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", 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())); sourceVM, convertHost.getId(), convertHost.getName()));
@ -1926,9 +2001,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
} }
cmd.setThreadsCountToExportOvf(noOfThreads); 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; Answer convertAnswer;
try { try {
convertAnswer = agentManager.send(convertHost.getId(), cmd); convertAnswer = agentManager.send(convertHost.getId(), convertInstanceCommand);
} catch (AgentUnavailableException | OperationTimedoutException e) { } catch (AgentUnavailableException | OperationTimedoutException e) {
String err = String.format("Could not send the convert instance command to host %s (%s) due to: %s", String err = String.format("Could not send the convert instance command to host %s (%s) due to: %s",
convertHost.getId(), convertHost.getName(), e.getMessage()); convertHost.getId(), convertHost.getName(), e.getMessage());
@ -1942,7 +2026,30 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
logger.error(err); logger.error(err);
throw new CloudRuntimeException(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) { private List<StoragePoolVO> findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) {
@ -1974,7 +2081,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId) { protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster,
HostVO convertHost,
Long convertStoragePoolId) {
if (convertStoragePoolId != null) { if (convertStoragePoolId != null) {
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId); StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
if (selectedStoragePool == null) { 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 " + 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())); "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) { 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())); 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) { } 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.FirewallServiceProvider;
import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.element.VirtualRouterElement;
import com.cloud.network.element.VpcVirtualRouterElement; import com.cloud.network.element.VpcVirtualRouterElement;
import com.cloud.network.rules.FirewallManager;
import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRule.Purpose;
import com.cloud.network.rules.FirewallRuleVO; 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.AccountManager;
import com.cloud.user.DomainManager; import com.cloud.user.DomainManager;
import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ComponentContext;
import junit.framework.Assert;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; 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.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -47,6 +44,7 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
@ -62,40 +60,8 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class FirewallManagerTest { public class FirewallManagerTest {
private Logger logger = LogManager.getLogger(FirewallManagerTest.class);
private AutoCloseable closeable; 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 @Mock
AccountManager _accountMgr; AccountManager _accountMgr;
@Mock @Mock
@ -111,12 +77,30 @@ public class FirewallManagerTest {
@Mock @Mock
FirewallRulesDao _firewallDao; FirewallRulesDao _firewallDao;
@Spy
@InjectMocks @InjectMocks
FirewallManager _firewallMgr = new FirewallManagerImpl(); FirewallManagerImpl _firewallMgr;
FirewallRule fwRule50to150;
FirewallRule fwRule100to200;
FirewallRule fwRule151to200;
FirewallRule pfRule50to150;
FirewallRule pfRule100to200;
FirewallRule pfRule151to200;
@Before @Before
public void initMocks() { public void initMocks() {
closeable = MockitoAnnotations.openMocks(this); 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 @After
@ -124,6 +108,11 @@ public class FirewallManagerTest {
closeable.close(); 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") @Ignore("Requires database to be set up")
@Test @Test
public void testApplyRules() { 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.GetRemoteVmsCommand;
import com.cloud.agent.api.GetUnmanagedInstancesAnswer; import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
import com.cloud.agent.api.GetUnmanagedInstancesCommand; 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.agent.api.to.DataStoreTO;
import com.cloud.configuration.Resource; import com.cloud.configuration.Resource;
import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVO;
@ -116,6 +118,7 @@ import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState; import com.cloud.resource.ResourceState;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
@ -371,9 +374,9 @@ public class UnmanagedVMsManagerImplTest {
doNothing().when(networkModel).checkNetworkPermissions(any(Account.class), any(Network.class)); doNothing().when(networkModel).checkNetworkPermissions(any(Account.class), any(Network.class));
NicProfile profile = Mockito.mock(NicProfile.class); NicProfile profile = Mockito.mock(NicProfile.class);
Integer deviceId = 100; Integer deviceId = 100;
Pair<NicProfile, Integer> pair = new Pair<NicProfile, Integer>(profile, deviceId); 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), anyBoolean())).thenReturn(pair); 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(anyLong())).thenReturn(volumes); when(volumeDao.findByInstance(Mockito.anyLong())).thenReturn(volumes);
List<UserVmResponse> userVmResponses = new ArrayList<>(); List<UserVmResponse> userVmResponses = new ArrayList<>();
UserVmResponse userVmResponse = new UserVmResponse(); UserVmResponse userVmResponse = new UserVmResponse();
userVmResponse.setInstanceName(instance.getName()); userVmResponse.setInstanceName(instance.getName());
@ -597,6 +600,7 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = mock(ClusterVO.class); ClusterVO cluster = mock(ClusterVO.class);
when(cluster.getId()).thenReturn(clusterId); when(cluster.getId()).thenReturn(clusterId);
when(cluster.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(cluster.getDataCenterId()).thenReturn(zoneId); when(cluster.getDataCenterId()).thenReturn(zoneId);
when(clusterDao.findById(clusterId)).thenReturn(cluster); when(clusterDao.findById(clusterId)).thenReturn(cluster);
@ -610,6 +614,7 @@ public class UnmanagedVMsManagerImplTest {
when(importVmCmd.getHostIp()).thenReturn(host); when(importVmCmd.getHostIp()).thenReturn(host);
when(importVmCmd.getNicNetworkList()).thenReturn(Map.of("NIC 1", networkId)); when(importVmCmd.getNicNetworkList()).thenReturn(Map.of("NIC 1", networkId));
when(importVmCmd.getConvertInstanceHostId()).thenReturn(null); when(importVmCmd.getConvertInstanceHostId()).thenReturn(null);
when(importVmCmd.getImportInstanceHostId()).thenReturn(null);
when(importVmCmd.getConvertStoragePoolId()).thenReturn(null); when(importVmCmd.getConvertStoragePoolId()).thenReturn(null);
NetworkVO networkVO = Mockito.mock(NetworkVO.class); NetworkVO networkVO = Mockito.mock(NetworkVO.class);
@ -631,10 +636,14 @@ public class UnmanagedVMsManagerImplTest {
when(convertHost.getId()).thenReturn(convertHostId); when(convertHost.getId()).thenReturn(convertHostId);
when(convertHost.getName()).thenReturn("KVM-Convert-Host"); when(convertHost.getName()).thenReturn("KVM-Convert-Host");
when(convertHost.getType()).thenReturn(Host.Type.Routing); when(convertHost.getType()).thenReturn(Host.Type.Routing);
when(convertHost.getDataCenterId()).thenReturn(zoneId);
when(convertHost.getClusterId()).thenReturn(clusterId); when(convertHost.getClusterId()).thenReturn(clusterId);
if (selectConvertHost) { if (selectConvertHost) {
when(importVmCmd.getConvertInstanceHostId()).thenReturn(convertHostId); when(importVmCmd.getConvertInstanceHostId()).thenReturn(convertHostId);
when(importVmCmd.getImportInstanceHostId()).thenReturn(convertHostId);
when(hostDao.findById(convertHostId)).thenReturn(convertHost); when(hostDao.findById(convertHostId)).thenReturn(convertHost);
} else {
when(hostDao.listByClusterAndHypervisorType(clusterId, Hypervisor.HypervisorType.KVM)).thenReturn(List.of(convertHost));
} }
DataStoreTO dataStoreTO = mock(DataStoreTO.class); DataStoreTO dataStoreTO = mock(DataStoreTO.class);
@ -695,10 +704,14 @@ public class UnmanagedVMsManagerImplTest {
} }
ConvertInstanceAnswer convertInstanceAnswer = mock(ConvertInstanceAnswer.class); 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.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) { if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) {
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ConvertInstanceCommand.class))).thenReturn(convertInstanceAnswer); 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)) { try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
@ -762,6 +775,7 @@ public class UnmanagedVMsManagerImplTest {
} }
} }
@Test
public void testImportVmFromVmwareToKvmExistingVcenter() throws OperationTimedoutException, AgentUnavailableException { public void testImportVmFromVmwareToKvmExistingVcenter() throws OperationTimedoutException, AgentUnavailableException {
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, false, false); baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, false, false);
} }
@ -814,7 +828,7 @@ public class UnmanagedVMsManagerImplTest {
long poolId = 1L; long poolId = 1L;
when(primaryDataStoreDao.findById(poolId)).thenReturn(null); when(primaryDataStoreDao.findById(poolId)).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -822,20 +836,35 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = getClusterForTests(); ClusterVO cluster = getClusterForTests();
long poolId = 1L; long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class); StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(100L); when(pool.getClusterId()).thenReturn(100L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); 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) @Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationLocalStoragePoolInvalid() { public void testSelectInstanceConversionTemporaryLocationLocalStoragePoolInvalid() {
ClusterVO cluster = getClusterForTests(); ClusterVO cluster = getClusterForTests();
long poolId = 1L; long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class); StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST); when(pool.getScope()).thenReturn(ScopeType.HOST);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -843,18 +872,250 @@ public class UnmanagedVMsManagerImplTest {
ClusterVO cluster = getClusterForTests(); ClusterVO cluster = getClusterForTests();
long poolId = 1L; long poolId = 1L;
StoragePoolVO pool = mock(StoragePoolVO.class); StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(1L); when(pool.getClusterId()).thenReturn(1L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD); when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() { public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
ClusterVO cluster = getClusterForTests(); ClusterVO cluster = getClusterForTests();
Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null); when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, 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 @Test

View File

@ -1467,7 +1467,10 @@ class CsForwardingRules(CsDataBag):
self.fw.append(["filter", "", fw7]) self.fw.append(["filter", "", fw7])
def forward_vpc(self, rule): 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": if not rule["protocol"] == "any":
fw_prerout_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"]) fw_prerout_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"])
if not rule["public_ports"] == "any": if not rule["public_ports"] == "any":
@ -1476,7 +1479,10 @@ class CsForwardingRules(CsDataBag):
if not rule["internal_ports"] == "any": if not rule["internal_ports"] == "any":
fw_prerout_rule += ":" + self.portsToString(rule["internal_ports"], "-") 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": if not rule["protocol"] == "any":
fw_output_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"]) fw_output_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"])
if not rule["public_ports"] == "any": if not rule["public_ports"] == "any":

View File

@ -379,7 +379,7 @@ class CsNetfilter(object):
return self.rule return self.rule
def to_str(self, delete=False): 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 is important
order = ['-A', '-s', '-d', '!_-d', '-i', '!_-i', '-p', '-m', '-m2', '--icmp-type', '--state', order = ['-A', '-s', '-d', '!_-d', '-i', '!_-i', '-p', '-m', '-m2', '--icmp-type', '--state',
'--dport', '--destination-port', '-o', '!_-o', '-j', '--set-xmark', '--checksum', '--dport', '--destination-port', '-o', '!_-o', '-j', '--set-xmark', '--checksum',

View File

@ -16,6 +16,8 @@
# under the License. # under the License.
import logging
def merge(dbag, rules): def merge(dbag, rules):
for rule in rules["rules"]: for rule in rules["rules"]:
source_ip = rule["source_ip_address"] source_ip = rule["source_ip_address"]
@ -33,6 +35,8 @@ def merge(dbag, rules):
newrule["public_ports"] = rule["source_port_range"] newrule["public_ports"] = rule["source_port_range"]
newrule["internal_ports"] = rule["destination_port_range"] newrule["internal_ports"] = rule["destination_port_range"]
newrule["protocol"] = rule["protocol"] newrule["protocol"] = rule["protocol"]
if "source_cidr_list" in rule:
newrule["source_cidr_list"] = rule["source_cidr_list"]
if not revoke: if not revoke:
if rules["type"] == "staticnatrules": if rules["type"] == "staticnatrules":
@ -59,7 +63,7 @@ def merge(dbag, rules):
for forward in dbag[source_ip]: for forward in dbag[source_ip]:
if ruleCompare(forward, newrule): if ruleCompare(forward, newrule):
index = dbag[source_ip].index(forward) 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: if not index == -1:
del dbag[source_ip][index] del dbag[source_ip][index]
@ -74,4 +78,18 @@ def ruleCompare(ruleA, ruleB):
return ruleA["public_ip"] == ruleB["public_ip"] return ruleA["public_ip"] == ruleB["public_ip"]
elif ruleA["type"] == "forward": elif ruleA["type"] == "forward":
return ruleA["public_ip"] == ruleB["public_ip"] and ruleA["public_ports"] == ruleB["public_ports"] \ 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.for": "for",
"label.forbidden": "Forbidden", "label.forbidden": "Forbidden",
"label.forced": "Force", "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.stop": "Force stop",
"label.force.reboot": "Force reboot", "label.force.reboot": "Force reboot",
"label.forceencap": "Force UDP encapsulation of ESP packets", "label.forceencap": "Force UDP encapsulation of ESP packets",
@ -1564,6 +1564,7 @@
"label.noselect": "No thanks", "label.noselect": "No thanks",
"label.not.found": "Not found", "label.not.found": "Not found",
"label.not.suitable": "Not suitable", "label.not.suitable": "Not suitable",
"label.not.supported": "Not supported",
"label.notifications": "Notifications", "label.notifications": "Notifications",
"label.nsx": "NSX", "label.nsx": "NSX",
"label.nsx.provider": "NSX Provider", "label.nsx.provider": "NSX Provider",
@ -2102,6 +2103,7 @@
"label.source": "Select Import-Export Source Hypervisor", "label.source": "Select Import-Export Source Hypervisor",
"label.source.based": "SourceBased", "label.source.based": "SourceBased",
"label.sourcecidr": "Source CIDR", "label.sourcecidr": "Source CIDR",
"label.sourcecidrlist": "Source CIDR list",
"label.sourcehost": "Source host", "label.sourcehost": "Source host",
"label.sourceipaddress": "Source IP address", "label.sourceipaddress": "Source IP address",
"label.sourceipaddressnetworkid": "Network ID of 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.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.disk.offering": "Please select a disk offering for disk",
"message.select.end.date.and.time": "Select an end date & time.", "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.kvm.host.instance.conversion": "(Optional) Select a KVM host in the zone 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.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.migration.policy": "Please select a migration policy.",
"message.select.nic.network": "Please select a Network for NIC", "message.select.nic.network": "Please select a Network for NIC",
"message.select.security.groups": "Please select security group(s) for your new Instance.", "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.choose.saml.identity": "Escolha um provedor de identidade SAML",
"label.cidr": "CIDR", "label.cidr": "CIDR",
"label.cidr.destination.network": "CIDR da rede de destino", "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.ip.address": "Endere\u00e7o IP do Nexus 1000v",
"label.cisco.nexus1000v.password": "Senha do Nexus 1000v", "label.cisco.nexus1000v.password": "Senha do Nexus 1000v",
"label.cisco.nexus1000v.username": "Usu\u00e1rio do Nexus 1000v", "label.cisco.nexus1000v.username": "Usu\u00e1rio do Nexus 1000v",
@ -1510,6 +1510,7 @@
"label.sockettimeout": "Tempo limite no socket", "label.sockettimeout": "Tempo limite no socket",
"label.source.based": "SourceBased", "label.source.based": "SourceBased",
"label.sourcecidr": "CIDR de origem", "label.sourcecidr": "CIDR de origem",
"label.sourcecidrlist": "Lista de CIDRs de origem",
"label.sourceipaddress": "Endere\u00e7o IP de origem", "label.sourceipaddress": "Endere\u00e7o IP de origem",
"label.sourcenat": "Source NAT", "label.sourcenat": "Source NAT",
"label.sourcenatsupported": "Suporte \u00e0 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__label">{{ $t('label.cpu') }}</div>
<div class="resource-detail-item__details"> <div class="resource-detail-item__details">
<appstore-outlined /> <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> <span v-else>{{ resource.cputotal }}</span>
<a-tag v-if="resource.arch" style="margin-left: 10px"> <a-tag v-if="resource.arch" style="margin-left: 10px">
{{ resource.arch }} {{ resource.arch }}
@ -233,7 +240,16 @@
<div class="resource-detail-item" v-else-if="resource.memorytotalgb"> <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__label">{{ $t('label.memory') }}</div>
<div class="resource-detail-item__details"> <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>
<div> <div>
<span v-if="resource.memoryusedgb"> <span v-if="resource.memoryusedgb">

View File

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

View File

@ -37,7 +37,7 @@
</div> </div>
<div class="form"> <div class="form">
<div class="form__item" ref="newCidrList"> <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> <a-input v-model:value="newRule.cidrlist"></a-input>
</div> </div>
<div class="form__item"> <div class="form__item">
@ -126,7 +126,7 @@
:rowKey="record => record.id"> :rowKey="record => record.id">
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'cidrlist'"> <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>
<template v-if="column.key === 'algorithm'"> <template v-if="column.key === 'algorithm'">
{{ returnAlgorithmName(record.algorithm) }} {{ returnAlgorithmName(record.algorithm) }}
@ -808,7 +808,7 @@ export default {
}, },
{ {
key: 'cidrlist', key: 'cidrlist',
title: this.$t('label.cidrlist') title: this.$t('label.sourcecidrlist')
}, },
{ {
key: 'protocol', 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-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
</a-select> </a-select>
</div> </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__item" style="margin-left: auto;">
<div class="form__label">{{ $t('label.add.vm') }}</div> <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> <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'"> <template v-if="column.key === 'protocol'">
{{ getCapitalise(record.protocol) }} {{ getCapitalise(record.protocol) }}
</template> </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'"> <template v-if="column.key === 'vm'">
<div><desktop-outlined/> <div><desktop-outlined/>
<router-link <router-link
@ -334,9 +343,11 @@ import Status from '@/components/widgets/Status'
import TooltipButton from '@/components/widgets/TooltipButton' import TooltipButton from '@/components/widgets/TooltipButton'
import BulkActionView from '@/components/view/BulkActionView' import BulkActionView from '@/components/view/BulkActionView'
import eventBus from '@/config/eventBus' import eventBus from '@/config/eventBus'
import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
export default { export default {
components: { components: {
TooltipLabel,
Status, Status,
TooltipButton, TooltipButton,
BulkActionView BulkActionView
@ -399,6 +410,11 @@ export default {
key: 'protocol', key: 'protocol',
title: this.$t('label.protocol') title: this.$t('label.protocol')
}, },
{
key: 'cidrlist',
title: this.$t('label.sourcecidrlist'),
hidden: !this.isVPC()
},
{ {
title: this.$t('label.state'), title: this.$t('label.state'),
dataIndex: 'state' dataIndex: 'state'
@ -411,7 +427,7 @@ export default {
key: 'actions', key: 'actions',
title: this.$t('label.actions') title: this.$t('label.actions')
} }
], ].filter(item => !item.hidden),
tiers: { tiers: {
loading: false, loading: false,
data: [] data: []
@ -450,7 +466,8 @@ export default {
vmPage: 1, vmPage: 1,
vmPageSize: 10, vmPageSize: 10,
vmCount: 0, vmCount: 0,
searchQuery: null searchQuery: null,
cidrlist: ''
} }
}, },
computed: { computed: {
@ -458,6 +475,9 @@ export default {
return this.selectedRowKeys.length > 0 return this.selectedRowKeys.length > 0
} }
}, },
beforeCreate () {
this.apiParams = this.$getApiParams('createPortForwardingRule')
},
created () { created () {
console.log(this.resource) console.log(this.resource)
this.initForm() this.initForm()
@ -830,6 +850,9 @@ export default {
onSearch (value) { onSearch (value) {
this.searchQuery = value this.searchQuery = value
this.fetchVirtualMachines() this.fetchVirtualMachines()
},
isVPC () {
return 'vpcid' in this.resource
} }
} }
} }

View File

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

View File

@ -164,6 +164,18 @@
@handle-checkselectpair-change="updateSelectedKvmHostForConversion" @handle-checkselectpair-change="updateSelectedKvmHostForConversion"
/> />
</a-form-item> </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"> <a-form-item name="convertstorageoption" ref="convertstorageoption">
<check-box-select-pair <check-box-select-pair
layout="vertical" layout="vertical"
@ -494,7 +506,9 @@ export default {
switches: {}, switches: {},
loading: false, loading: false,
kvmHostsForConversion: [], kvmHostsForConversion: [],
kvmHostsForImporting: [],
selectedKvmHostForConversion: null, selectedKvmHostForConversion: null,
selectedKvmHostForImporting: null,
storageOptionsForConversion: [ storageOptionsForConversion: [
{ {
id: 'secondary', id: 'secondary',
@ -730,6 +744,7 @@ export default {
page: 1 page: 1
}) })
this.fetchKvmHostsForConversion() this.fetchKvmHostsForConversion()
this.fetchKvmHostsForImporting()
if (this.resource?.disk?.length > 1) { if (this.resource?.disk?.length > 1) {
this.updateSelectedRootDisk() this.updateSelectedRootDisk()
} }
@ -914,6 +929,25 @@ export default {
} }
}, },
fetchKvmHostsForConversion () { 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', { api('listHosts', {
clusterid: this.cluster.id, clusterid: this.cluster.id,
hypervisor: this.cluster.hypervisortype, hypervisor: this.cluster.hypervisortype,
@ -921,20 +955,22 @@ export default {
state: 'Up', state: 'Up',
resourcestate: 'Enabled' resourcestate: 'Enabled'
}).then(json => { }).then(json => {
this.kvmHostsForConversion = json.listhostsresponse.host || [] this.kvmHostsForImporting = json.listhostsresponse.host || []
this.kvmHostsForConversion.map(host => {
if (host.instanceconversionsupported !== null && host.instanceconversionsupported !== undefined && host.instanceconversionsupported) {
host.name = host.name + ' (' + this.$t('label.supported') + ')'
}
})
}) })
}, },
fetchStoragePoolsForConversion () { fetchStoragePoolsForConversion () {
if (this.selectedStorageOptionForConversion === 'primary') { if (this.selectedStorageOptionForConversion === 'primary') {
api('listStoragePools', { const params = {
zoneid: this.cluster.zoneid, clusterid: this.cluster.id,
status: 'Up' 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 || [] this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
}) })
} else if (this.selectedStorageOptionForConversion === 'local') { } 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) { updateSelectedKvmHostForConversion (clusterid, checked, value) {
if (checked) { if (checked) {
this.selectedKvmHostForConversion = value this.selectedKvmHostForConversion = value
@ -1099,6 +1143,9 @@ export default {
if (this.selectedKvmHostForConversion) { if (this.selectedKvmHostForConversion) {
params.convertinstancehostid = this.selectedKvmHostForConversion params.convertinstancehostid = this.selectedKvmHostForConversion
} }
if (this.selectedKvmHostForImporting) {
params.importinstancehostid = this.selectedKvmHostForImporting
}
if (this.selectedStoragePoolForConversion) { if (this.selectedStoragePoolForConversion) {
params.convertinstancepoolid = this.selectedStoragePoolForConversion params.convertinstancepoolid = this.selectedStoragePoolForConversion
} }

View File

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