package org.ovirt.engine.core.bll.validator;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.bll.network.cluster.ManagementNetworkUtil;
import org.ovirt.engine.core.common.businessentities.IscsiBond;
import org.ovirt.engine.core.common.businessentities.Nameable;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.network.Network;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.di.Injector;
import org.ovirt.engine.core.utils.NetworkUtils;
import org.ovirt.engine.core.utils.ReplacementUtils;
/**
* This class is used to validate different traits of a network on the data center level. <br>
* <br>
* Usage: instantiate on a per-network basis, passing the network to be validated as an argument to the constructor.
*/
public class NetworkValidator {
private final VmDao vmDao;
protected final Network network;
private StoragePool dataCenter;
private List<Network> networks;
private List<VM> vms;
private List<VmTemplate> templates;
public NetworkValidator(VmDao vmDao, Network network) {
this.network = network;
this.vmDao = vmDao;
}
protected DbFacade getDbFacade() {
return DbFacade.getInstance();
}
protected StoragePool getDataCenter() {
if (dataCenter == null) {
dataCenter = getDbFacade().getStoragePoolDao().get(network.getDataCenterId());
}
return dataCenter;
}
/**
* @return All existing networks in same data center.
*/
protected List<Network> getNetworks() {
if (networks == null) {
networks = getDbFacade().getNetworkDao().getAllForDataCenter(network.getDataCenterId());
}
return networks;
}
/**
* @return An error iff STP is specified for a non-VM network.
*/
public ValidationResult stpForVmNetworkOnly() {
return ValidationResult.failWith(EngineMessage.NON_VM_NETWORK_CANNOT_SUPPORT_STP)
.unless(network.isVmNetwork() || !network.getStp());
}
/**
* @return {@link ValidationResult#VALID}, This method exists only so it could be overridden.
*/
public ValidationResult mtuValid() {
return ValidationResult.VALID;
}
/**
* @return An error iff a different network in the data center is already using the specified VLAN ID.
*/
public ValidationResult vlanIdNotUsed() {
if (NetworkUtils.isVlan(network)) {
for (Network otherNetwork : getNetworks()) {
if (NetworkUtils.isVlan(otherNetwork)
&& otherNetwork.getVlanId().equals(network.getVlanId())
&& !otherNetwork.getId().equals(network.getId())) {
return new ValidationResult(EngineMessage.NETWORK_VLAN_IN_USE,
String.format("$vlanId %d", network.getVlanId()));
}
}
}
return ValidationResult.VALID;
}
/**
* @return An error iff network is named as if it were a bond.
*/
public ValidationResult networkPrefixValid() {
return ValidationResult.failWith(EngineMessage.NETWORK_CANNOT_CONTAIN_BOND_NAME)
.when(network.getName().toLowerCase().startsWith("bond"));
}
/**
* @return An error iff the data center to which the network belongs doesn't exist.
*/
public ValidationResult dataCenterExists() {
return ValidationResult.failWith(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_EXIST)
.when(getDataCenter() == null);
}
/**
* @return An error iff the network isn't set.
*/
public ValidationResult networkIsSet(Guid networkId) {
EngineMessage engineMessage = EngineMessage.NETWORK_HAVING_ID_NOT_EXISTS;
return ValidationResult.failWith(engineMessage,
ReplacementUtils.getVariableAssignmentString(engineMessage, String.valueOf(networkId)))
.when(network == null);
}
/**
* @return An error if the network's name is already used by another network in the same data center.
*/
public ValidationResult networkNameNotUsed() {
for (Network otherNetwork : getNetworks()) {
if (otherNetwork.getName().equals(network.getName()) &&
!otherNetwork.getId().equals(network.getId())) {
return new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_NETWORK_NAME_IN_USE,
getNetworkNameReplacement());
}
}
return ValidationResult.VALID;
}
public ValidationResult notManagementNetwork() {
final boolean isManagementNetwork = isManagementNetwork();
return getManagementNetworkValidationResult(isManagementNetwork);
}
protected boolean isManagementNetwork() {
return getManagementNetworkUtil().isManagementNetwork(network.getId());
}
private ValidationResult getManagementNetworkValidationResult(final boolean isManagementNetwork) {
return isManagementNetwork
? new ValidationResult(EngineMessage.NETWORK_CANNOT_REMOVE_MANAGEMENT_NETWORK,
getNetworkNameReplacement())
: ValidationResult.VALID;
}
protected ManagementNetworkUtil getManagementNetworkUtil() {
return Injector.get(ManagementNetworkUtil.class);
}
public ValidationResult notRemovingManagementNetwork() {
return isManagementNetwork()
? new ValidationResult(EngineMessage.NETWORK_CANNOT_REMOVE_MANAGEMENT_NETWORK,
getNetworkNameReplacement())
: ValidationResult.VALID;
}
public ValidationResult notIscsiBondNetwork() {
List<IscsiBond> iscsiBonds = getDbFacade().getIscsiBondDao().getIscsiBondsByNetworkId(network.getId());
if (!iscsiBonds.isEmpty()) {
Collection<String> replaceNameables = ReplacementUtils.replaceWithNameable("IscsiBonds", iscsiBonds);
replaceNameables.add(getNetworkNameReplacement());
return new ValidationResult(EngineMessage.NETWORK_CANNOT_REMOVE_ISCSI_BOND_NETWORK,
replaceNameables);
}
return ValidationResult.VALID;
}
protected String getNetworkNameReplacement() {
return String.format("$NetworkName %s", network.getName());
}
protected Collection<String> getEntitiesNames(List<? extends Nameable> entities) {
List<String> result = new ArrayList<>(entities.size());
for (Nameable itemName : entities) {
result.add(itemName.getName());
}
return result;
}
/**
* @return An error iff the network is in use by any VMs.
*/
public ValidationResult networkNotUsedByVms() {
List<VM> vms = getVms();
return networkNotUsedByVms(getEntitiesNames(vms));
}
public ValidationResult networkNotUsedByVms(Collection<String> vmNames) {
return new PluralMessages(EngineMessage.VAR__ENTITIES__VM, EngineMessage.VAR__ENTITIES__VMS)
.getNetworkInUse(vmNames);
}
/**
* @return An error iff the network is in use by any hosts.
*/
public ValidationResult networkNotUsedByHosts() {
List<VDS> allForNetwork = getDbFacade().getVdsDao().getAllForNetwork(network.getId());
return new PluralMessages(EngineMessage.VAR__ENTITIES__HOST, EngineMessage.VAR__ENTITIES__HOSTS)
.getNetworkInUse(getEntitiesNames(allForNetwork));
}
/**
* @return An error iff the network is in use by any templates.
*/
public ValidationResult networkNotUsedByTemplates() {
return new PluralMessages(EngineMessage.VAR__ENTITIES__VM_TEMPLATE, EngineMessage.VAR__ENTITIES__VM_TEMPLATES)
.getNetworkInUse(getEntitiesNames(getTemplates()));
}
/**
* @return An error iff the QoS entity attached to the network isn't null, but doesn't exist in the database or
* belongs to the wrong DC.
*/
public ValidationResult qosExistsInDc() {
HostNetworkQosValidator qosValidator =
new HostNetworkQosValidator(getDbFacade().getHostNetworkQosDao().get(network.getQosId()));
ValidationResult res = qosValidator.qosExists();
return (res == ValidationResult.VALID) ? qosValidator.consistentDataCenter() : res;
}
public ValidationResult notLabeled() {
return ValidationResult.failWith(EngineMessage.ACTION_TYPE_FAILED_NETWORK_ALREADY_LABELED)
.when(NetworkUtils.isLabeled(network));
}
public ValidationResult notExternalNetwork() {
return ValidationResult.failWith(EngineMessage.ACTION_TYPE_FAILED_NOT_SUPPORTED_FOR_EXTERNAL_NETWORK)
.when(network.isExternal());
}
protected List<VM> getVms() {
if (vms == null) {
vms = getVmDao().getAllForNetwork(network.getId());
}
return vms;
}
protected VmDao getVmDao() {
return vmDao;
}
protected List<VmTemplate> getTemplates() {
if (templates == null) {
templates = getDbFacade().getVmTemplateDao().getAllForNetwork(network.getId());
}
return templates;
}
public boolean canNetworkCompatibilityBeDecreased() {
return mtuValid().isValid();
}
public void setDataCenter(StoragePool dataCenter) {
this.dataCenter = dataCenter;
}
protected static class PluralMessages {
private final EngineMessage entitiesReplacementPlural;
private final EngineMessage entitiesReplacementSingular;
public PluralMessages(EngineMessage entitiesReplacementSingular, EngineMessage entitiesReplacementPlural) {
this.entitiesReplacementPlural = entitiesReplacementPlural;
this.entitiesReplacementSingular = entitiesReplacementSingular;
}
/**
* @param names names of entities using the network
*/
public ValidationResult getNetworkInUse(Collection<String> names) {
if (names.isEmpty()) {
return ValidationResult.VALID;
}
int numberOfEntities = names.size();
boolean useSingular = numberOfEntities == 1;
return useSingular ? getNetworkInUseSingular(names) : getNetworkInUsePlural(names);
}
private ValidationResult getNetworkInUsePlural(Collection<String> names) {
EngineMessage engineMessage = EngineMessage.ACTION_TYPE_FAILED_NETWORK_IN_MANY_USES;
List<String> replacements = Stream.concat(
ReplacementUtils.getListVariableAssignmentString(engineMessage, names).stream(),
Stream.of(entitiesReplacementPlural.name()))
.collect(toList());
return new ValidationResult(engineMessage, replacements);
}
private ValidationResult getNetworkInUseSingular(Collection<String> names) {
String name = names.iterator().next();
EngineMessage engineMessage = EngineMessage.ACTION_TYPE_FAILED_NETWORK_IN_ONE_USE;
return new ValidationResult(engineMessage,
ReplacementUtils.getVariableAssignmentString(engineMessage, name),
entitiesReplacementSingular.name());
}
}
}