package org.ovirt.engine.core.bll.storage.connection; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.Backend; import org.ovirt.engine.core.bll.InternalCommandAttribute; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.validator.storage.StorageConnectionValidator; import org.ovirt.engine.core.common.action.StorageServerConnectionParametersBase; import org.ovirt.engine.core.common.businessentities.StorageServerConnections; import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; import org.ovirt.engine.core.common.businessentities.storage.StorageType; import org.ovirt.engine.core.common.constants.StorageConstants; import org.ovirt.engine.core.common.errors.EngineFault; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.common.validation.NfsMountPointConstraint; import org.ovirt.engine.core.common.vdscommands.StorageServerConnectionManagementVDSParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; import org.ovirt.engine.core.utils.StringMapUtils; @InternalCommandAttribute public class ConnectStorageToVdsCommand<T extends StorageServerConnectionParametersBase> extends StorageServerConnectionCommandBase<T> { private static final String KEY_VALUE_SEPARATOR = "="; @Inject private GlusterVolumeDao glusterVolumeDao; public ConnectStorageToVdsCommand(T parameters, CommandContext cmdContext) { super(parameters, cmdContext); } public ConnectStorageToVdsCommand(Guid commandId) { super(commandId); } @Override protected void executeCommand() { Pair<Boolean, Integer> result = connectHostToStorage(); setSucceeded(result.getFirst()); if (!result.getFirst()) { setErrorMessageAtReturn(result); } } private void setErrorMessageAtReturn(Pair<Boolean, Integer> result) { EngineFault fault = new EngineFault(); fault.setError(result.getSecond()); if (fault.getError() != null) { fault.setMessage( Backend.getInstance() .getVdsErrorsTranslator() .translateErrorTextSingle(fault.getError().toString())); } getReturnValue().setFault(fault); } protected Pair<Boolean, Integer> connectHostToStorage() { List<StorageServerConnections> connections = Arrays.asList(getConnection()); if (getConnection().getStorageType() == StorageType.ISCSI) { connections = ISCSIStorageHelper.updateIfaces(connections, getVds().getId()); } Map<String, String> result = (HashMap<String, String>) runVdsCommand( VDSCommandType.ConnectStorageServer, new StorageServerConnectionManagementVDSParameters(getVds().getId(), Guid.Empty, getConnection().getStorageType(), connections)).getReturnValue(); return new Pair<>(StorageHelperDirector.getInstance() .getItem(getConnection().getStorageType()) .isConnectSucceeded(result, connections), Integer.parseInt(result.values().iterator().next())); } protected boolean isValidStorageConnectionPort(String port) { return !StringUtils.isEmpty(port) && StringUtils.isNumeric(port) && Integer.parseInt(port) > 0; } protected boolean isValidConnection(StorageServerConnections conn) { StorageType storageType = conn.getStorageType(); if (storageType == StorageType.NFS && !new NfsMountPointConstraint().isValid(conn.getConnection(), null)) { return failValidation(EngineMessage.VALIDATION_STORAGE_CONNECTION_INVALID); } if (storageType == StorageType.POSIXFS && StringUtils.isEmpty(conn.getVfsType())) { return failValidation(EngineMessage.VALIDATION_STORAGE_CONNECTION_EMPTY_VFSTYPE); } if ((storageType == StorageType.POSIXFS || storageType == StorageType.NFS) && !validate(validateMountOptions())) { return false; } if (storageType == StorageType.ISCSI) { if (StringUtils.isEmpty(conn.getIqn())) { return failValidation(EngineMessage.VALIDATION_STORAGE_CONNECTION_EMPTY_IQN); } if (!isValidStorageConnectionPort(conn.getPort())) { return failValidation(EngineMessage.VALIDATION_STORAGE_CONNECTION_INVALID_PORT); } } if (storageType == StorageType.GLUSTERFS) { StorageConnectionValidator validator = new StorageConnectionValidator(conn); if (!validate(validateVolumeIdAndUpdatePath(conn)) || !validate(validator.canVDSConnectToGlusterfs(getVds()))) { return false; } } if (checkIsConnectionFieldEmpty(conn)) { return false; } return true; } private ValidationResult validateVolumeIdAndUpdatePath(StorageServerConnections connection) { if (connection.getGlusterVolumeId() != null) { GlusterVolumeEntity glusterVolume = glusterVolumeDao.getById(connection.getGlusterVolumeId()); if (glusterVolume == null || glusterVolume.getBricks().isEmpty()) { return new ValidationResult(EngineMessage.VALIDATION_STORAGE_CONNECTION_INVALID_GLUSTER_VOLUME); } Set<String> addressSet = new LinkedHashSet<>(); glusterVolume.getBricks().forEach( brick -> addressSet.add(brick.getNetworkId() != null && !brick.getNetworkAddress().isEmpty() ? brick.getNetworkAddress() : brick.getServerName())); String firstHost = (String) addressSet.toArray()[0]; // we don't want the firstHost repeated in backup-volfile-servers addressSet.remove(firstHost); connection.setConnection(firstHost + StorageConstants.GLUSTER_VOL_SEPARATOR + glusterVolume.getName()); String mountOptions = StorageConstants.GLUSTER_BACKUP_SERVERS_MNT_OPTION + KEY_VALUE_SEPARATOR + StringUtils.join(addressSet.toArray(), ':'); if (StringUtils.isBlank(connection.getMountOptions())) { connection.setMountOptions(mountOptions); } else if (!connection.getMountOptions().contains(StorageConstants.GLUSTER_BACKUP_SERVERS_MNT_OPTION)) { mountOptions = connection.getMountOptions().concat("," + mountOptions); connection.setMountOptions(mountOptions); } } return ValidationResult.VALID; } private static final List<String> NFS_MANAGED_OPTIONS = Arrays.asList("timeo", "retrans", "vfs_type", "protocol_version", "nfsvers", "vers", "minorversion", "addr", "clientaddr"); private static final List<String> POSIX_MANAGED_OPTIONS = Arrays.asList("vfs_type", "addr", "clientaddr"); protected ValidationResult validateMountOptions() { String mountOptions = getConnection().getMountOptions(); if (StringUtils.isBlank(mountOptions)) { return ValidationResult.VALID; } List<String> disallowedOptions = getConnection().getStorageType() == StorageType.POSIXFS ? POSIX_MANAGED_OPTIONS : NFS_MANAGED_OPTIONS; Map<String, String> optionsMap = StringMapUtils.string2Map(mountOptions); Set<String> optionsKeys = new HashSet<>(); for (String option : optionsMap.keySet()) { optionsKeys.add(option.toLowerCase()); } optionsKeys.retainAll(disallowedOptions); if (!optionsKeys.isEmpty()) { addValidationMessageVariable("invalidOptions", StringUtils.join(optionsKeys, ", ")); return new ValidationResult(EngineMessage.VALIDATION_STORAGE_CONNECTION_MOUNT_OPTIONS_CONTAINS_MANAGED_PROPERTY); } return ValidationResult.VALID; } }