package org.ovirt.engine.core.vdsbroker.vdsbroker;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.ovirt.engine.core.common.businessentities.BusinessEntityMap;
import org.ovirt.engine.core.common.businessentities.Entities;
import org.ovirt.engine.core.common.businessentities.network.Network;
import org.ovirt.engine.core.common.businessentities.network.NetworkAttachment;
import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface;
import org.ovirt.engine.core.common.utils.NetworkCommonUtils;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.network.NetworkAttachmentDao;
import org.ovirt.engine.core.utils.NetworkUtils;
public class HostNetworkAttachmentsPersister {
static final String INCONSISTENCY_NETWORK_IS_REPORTED_ON_DIFFERENT_NIC_THAN_WAS_SPECIFIED = "Inconsistency in current state and reported data: given network is reported on different nic than was specified.";
private final NetworkAttachmentDao networkAttachmentDao;
private final Guid hostId;
private final List<NetworkAttachment> userNetworkAttachments;
private final Set<Guid> userRemovedNetworkAttachments;
private final Map<String, VdsNetworkInterface> nicsByName;
/**
* map networkID-->NIC for each network assigned to one of given nics. Value of mapping is such mapped NIC.
*/
private final Map<Guid, VdsNetworkInterface> reportedNicsByNetworkId;
/**
* map networkID-->Network for each network assigned to one of given nics.
*/
private final Map<Guid, Network> reportedNetworksById;
private final BusinessEntityMap<Network> clusterNetworks;
public HostNetworkAttachmentsPersister(NetworkAttachmentDao networkAttachmentDao,
Guid hostId,
List<VdsNetworkInterface> nics,
List<NetworkAttachment> userNetworkAttachments,
Set<Guid> userRemovedNetworkAttachments,
List<Network> clusterNetworks) {
this.networkAttachmentDao = networkAttachmentDao;
this.hostId = hostId;
this.userNetworkAttachments = userNetworkAttachments;
this.userRemovedNetworkAttachments = userRemovedNetworkAttachments;
this.clusterNetworks = new BusinessEntityMap<>(clusterNetworks);
nicsByName = Entities.entitiesByName(nics);
reportedNetworksById = new HashMap<>();
reportedNicsByNetworkId = new HashMap<>();
initReportedNetworksAndNics(nics);
}
public HostNetworkAttachmentsPersister(NetworkAttachmentDao networkAttachmentDao,
Guid hostId,
List<VdsNetworkInterface> nics,
List<NetworkAttachment> userNetworkAttachments,
List<Network> clusterNetworks) {
this(networkAttachmentDao,
hostId,
nics,
userNetworkAttachments,
Collections.emptySet(),
clusterNetworks);
}
private void initReportedNetworksAndNics(List<VdsNetworkInterface> nics) {
for (VdsNetworkInterface nic : nics) {
if (nic.getNetworkName() != null) {
Network network = clusterNetworks.get(nic.getNetworkName());
if (network != null) {
reportedNetworksById.put(network.getId(), network);
reportedNicsByNetworkId.put(network.getId(), nic);
}
}
}
}
public void persistNetworkAttachments() {
List<NetworkAttachment> networkAttachments = networkAttachmentDao.getAllForHost(hostId);
removeInvalidNetworkAttachmentsFromDb(networkAttachments);
removeUserRemovedNetworkAttachments(networkAttachments);
persistOrUpdateUserNetworkAttachments(networkAttachments);
updateReportedNetworkAttachmentsNotMentionedInRequest(networkAttachments);
}
private void removeUserRemovedNetworkAttachments(List<NetworkAttachment> existingNetworkAttachments) {
for (Guid attachmentId : userRemovedNetworkAttachments) {
networkAttachmentDao.remove(attachmentId);
}
// Remove the attachment from the existing attachments list
for (Iterator<NetworkAttachment> iterator = existingNetworkAttachments.iterator(); iterator.hasNext();) {
Guid networkAttachmentId = iterator.next().getId();
if (userRemovedNetworkAttachments.contains(networkAttachmentId)) {
iterator.remove();
}
}
}
/**
* Adds new network attachments for reported networks from the host which weren't associated to a user attachment.
* Updates existing network attachments which network get reported on different nic.
* <br/>
* The network attachment's attributes will be inherited from the network interface on which it is configured.
* @param networkAttachments already existing or newly created network attachments
*/
private void updateReportedNetworkAttachmentsNotMentionedInRequest(List<NetworkAttachment> networkAttachments) {
for (VdsNetworkInterface nic : nicsByName.values()) {
String networkName = nic.getNetworkName();
if (networkName != null && clusterNetworks.containsKey(networkName)) {
NetworkAttachment networkAttachmentRelatedToNetwork =
getNetworkAttachmentRelatedToNetwork(networkAttachments, clusterNetworks.get(networkName));
boolean networkAttachmentRelatedToNetworkExist = networkAttachmentRelatedToNetwork != null;
if (networkAttachmentRelatedToNetworkExist) {
VdsNetworkInterface baseInterfaceNicOrThis = getBaseInterfaceNicOrThis(nic);
boolean networkMovedToDifferentNic =
!baseInterfaceNicOrThis.getId().equals(networkAttachmentRelatedToNetwork.getNicId());
if (networkMovedToDifferentNic) {
networkAttachmentRelatedToNetwork.setNicId(baseInterfaceNicOrThis.getId());
networkAttachmentRelatedToNetwork.setNicName(baseInterfaceNicOrThis.getName());
networkAttachmentDao.update(networkAttachmentRelatedToNetwork);
}
} else {
if (!nic.isPartOfBond()) {
createNetworkAttachmentForReportedNetworksNotHavingOne(nic, networkName);
}
}
}
}
}
private void createNetworkAttachmentForReportedNetworksNotHavingOne(VdsNetworkInterface nic, String networkName) {
NetworkAttachment networkAttachment =
new NetworkAttachment(getBaseInterfaceNicOrThis(nic),
clusterNetworks.get(networkName),
NetworkUtils.createIpConfigurationFromVdsNetworkInterface(nic));
networkAttachment.setId(Guid.newGuid());
networkAttachmentDao.save(networkAttachment);
}
private VdsNetworkInterface getBaseInterfaceNicOrThis(VdsNetworkInterface nic) {
return nic.getBaseInterface() == null ? nic : nicsByName.get(nic.getBaseInterface());
}
private NetworkAttachment getNetworkAttachmentRelatedToNetwork(List<NetworkAttachment> networkAttachments,
final Network network) {
return networkAttachments.stream()
.filter(networkAttachment -> network.getId().equals(networkAttachment.getNetworkId()))
.findFirst()
.orElse(null);
}
/**
* User provided network attachments, which relates to network configured on host, are inserted or updated
* into database and used to update passed in <code>networkAttachments</code> collection.
*/
private void persistOrUpdateUserNetworkAttachments(List<NetworkAttachment> networkAttachments) {
Map<Guid, NetworkAttachment> validDbAttachmentsById = Entities.businessEntitiesById(networkAttachments);
for (NetworkAttachment networkAttachment : userNetworkAttachments) {
if (networkConfiguredOnHost(networkAttachment.getNetworkId())) {
VdsNetworkInterface reportedNicToWhichIsNetworkAttached =
reportedNicsByNetworkId.get(networkAttachment.getNetworkId());
Guid reportedNicId = baseInterfaceOfNic(reportedNicToWhichIsNetworkAttached).getId();
/*this is needed for attaching network to newly created bond, where network attachment
does not have nicId set and that nic id is known only after vdsm call.
* */
if (networkAttachment.getNicId() == null) {
networkAttachment.setNicId(reportedNicId);
} else {
if (!networkAttachment.getNicId().equals(reportedNicId)) {
throw new IllegalStateException(
INCONSISTENCY_NETWORK_IS_REPORTED_ON_DIFFERENT_NIC_THAN_WAS_SPECIFIED);
}
}
boolean alreadyExistingAttachment = validDbAttachmentsById.containsKey(networkAttachment.getId());
if (alreadyExistingAttachment) {
networkAttachmentDao.update(networkAttachment);
networkAttachments.remove(validDbAttachmentsById.get(networkAttachment.getId()));
networkAttachments.add(networkAttachment);
} else {
networkAttachment.setId(Guid.newGuid());
networkAttachmentDao.save(networkAttachment);
networkAttachments.add(networkAttachment);
}
}
}
}
private VdsNetworkInterface baseInterfaceOfNic(VdsNetworkInterface nic) {
return nicsByName.get(NetworkCommonUtils.stripVlan(nic));
}
/**
* Removes all {@link org.ovirt.engine.core.common.businessentities.network.NetworkAttachment NetworkAttachments},
* which network aren't associated with any of given {@link #nicsByName nics}, both from
* <code>existingNetworkAttachments</code> and from db.
*
* @param existingNetworkAttachments all existing attachments.
*/
private void removeInvalidNetworkAttachmentsFromDb(List<NetworkAttachment> existingNetworkAttachments) {
for (Iterator<NetworkAttachment> iterator = existingNetworkAttachments.iterator(); iterator.hasNext(); ) {
NetworkAttachment networkAttachment = iterator.next();
boolean attachmentRelatesToNetworkNotReportedOnHost =
!networkConfiguredOnHost(networkAttachment.getNetworkId());
if (attachmentRelatesToNetworkNotReportedOnHost) {
networkAttachmentDao.remove(networkAttachment.getId());
iterator.remove();
}
}
}
/**
* @param networkId network ID.
* @return true if given network ID describes network bound to any reported NIC.
*/
private boolean networkConfiguredOnHost(Guid networkId) {
return reportedNetworksById.containsKey(networkId);
}
}