Java tutorial
/* * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.netvirt.federation.plugin; import com.google.common.collect.Maps; import com.google.common.util.concurrent.UncheckedExecutionException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.tuple.Pair; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.federation.plugin.spi.IFederationPluginIngress; import org.opendaylight.federation.service.api.federationutil.FederationUtils; import org.opendaylight.federation.service.common.api.EntityFederationMessage; import org.opendaylight.netvirt.federation.plugin.filters.FilterResult; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfo; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; public class FederationPluginIngress implements IFederationPluginIngress { private static final int MAX_TRANSACTION_SUBMIT_RETRIES = 2; private enum State { IDLE, COLLECTING; } private final Logger logger; private final IFederationSubscriptionMgr subscriptionMgr; private final DataBroker dataBroker; private final FederatedMappings federatedMappings; private volatile State state = State.IDLE; private volatile boolean aborted = false; private final Map<String, Collection<? extends DataObject>> fullSyncModifications = Maps.newConcurrentMap(); private final String remoteIp; static { FederationPluginUtils.initYangModules(); } public FederationPluginIngress(final IFederationSubscriptionMgr subscriptionMgr, final DataBroker dataBroker, String remoteIp, List<FederatedNetworkPair> networkPairs, List<FederatedAclPair> secGroupsPairs) { this.subscriptionMgr = subscriptionMgr; this.dataBroker = dataBroker; this.remoteIp = remoteIp; this.federatedMappings = new FederatedMappings(networkPairs, secGroupsPairs); logger = FederationUtils.createLogger(remoteIp, FederationPluginIngress.class); logger.info("Created new NetvirtPluginIngress instance for remoteIp {}", remoteIp); } @Override public synchronized void beginFullSync() { FederationPluginCounters.ingress_begin_tx.inc(); logger.info("Changing state to COLLECTING for remoteIP {}", remoteIp); state = State.COLLECTING; fullSyncModifications.clear(); } @Override @SuppressWarnings("checkstyle:IllegalCatch") public synchronized void endFullSync() { if (aborted) { FederationPluginCounters.ingress_full_sync_aborted.inc(); return; } int generationNumber = 1; RemoteSiteGenerationInfo currentGenerationNumber = FederationPluginUtils .getGenerationInfoForRemoteSite(dataBroker, remoteIp); if (currentGenerationNumber != null) { generationNumber = currentGenerationNumber.getGenerationNumber() + 1; } FederationPluginUtils.updateGenerationInfo(dataBroker, remoteIp, generationNumber); FederationPluginCounters.ingress_end_tx.inc(); try { processFullSyncModifications(generationNumber); logger.info("Changing state to IDLE for remoteIP {}", remoteIp); state = State.IDLE; } catch (Throwable t) { logger.error("Deciding to call Full Sync again because failed in processing pending modifications", t); subscriptionMgr.resubscribe(remoteIp); } } @Override public void fullSyncFailed() { FederationPluginCounters.ingress_full_sync_failed.inc(); logger.error("Full sync failed"); state = State.IDLE; } @Override public synchronized CompletableFuture<Void> abort() { logger.info("Abort Netvirt ingress plugin for remoteIp {}", remoteIp); aborted = true; return CompletableFuture.completedFuture(null); } @Override @SuppressWarnings({ "unchecked", "rawtypes", "checkstyle:IllegalCatch" }) public synchronized void consumeMsg(EntityFederationMessage msg) { if (aborted) { FederationPluginCounters.ingress_consume_msg_aborted.inc(); return; } FederationPluginCounters.ingress_consume_msg.inc(); LogicalDatastoreType datastoreType; try { datastoreType = LogicalDatastoreType.valueOf(msg.getDataStoreType()); } catch (IllegalArgumentException e) { logger.error("Failed to get datastore type for {}", msg.getDataStoreType()); return; } String listenerKey = msg.getMetadata(); if (listenerKey == null) { logger.error("Failed to get listener key for {}", msg.getInputClassType()); return; } ModificationType modificationType; try { modificationType = ModificationType.valueOf(msg.getModificationType()); } catch (IllegalArgumentException e) { logger.error("Invalid modification type {}", msg.getModificationType()); return; } DataObject dataObject = msg.getInput(); if (dataObject == null) { logger.error("Failed to create DataObject from msg {}", msg); return; } if (State.COLLECTING.equals(state)) { addFullSyncModification(listenerKey, dataObject, modificationType); } else { try { RemoteSiteGenerationInfo currentGenerationNumber = FederationPluginUtils .getGenerationInfoForRemoteSite(dataBroker, remoteIp); if (currentGenerationNumber != null && currentGenerationNumber.getGenerationNumber() != null) { processModification(listenerKey, dataObject, modificationType, currentGenerationNumber.getGenerationNumber()); } else { logger.error("Will call Full Sync again because there is no generation number set"); subscriptionMgr.resubscribe(remoteIp); } } catch (FederationCorruptedStateException e) { logger.error("Deciding to call Full Sync again because transactions failed too many times"); subscriptionMgr.resubscribe(remoteIp); } catch (Throwable t) { logger.error("Failed to process modification on listener key {}", listenerKey, t); } } } @Override public void resubscribe() { subscriptionMgr.resubscribe(remoteIp); } @Override public String getPluginType() { return FederationPluginConstants.PLUGIN_TYPE; } public synchronized void cleanShadowData() { logger.info("Removing all shadow entities for Netvirt ingress plugin for remoteIp {}", remoteIp); FederationPluginCleaner.removeOldGenerationFederatedEntities(dataBroker, Integer.MAX_VALUE, remoteIp); FederationPluginUtils.deleteGenerationInfo(dataBroker, remoteIp); } void subnetVpnAssociationUpdated(String subnetId, String vpnId) { FederationPluginCounters.ingress_subnet_vpn_association_changed.inc(); if (aborted) { FederationPluginCounters.ingress_subnet_vpn_association_aborted.inc(); return; } if (federatedMappings.containsConsumerSubnetId(subnetId)) { FederationPluginCounters.ingress_federated_subnet_vpn_association_changed.inc(); logger.info("Deciding to call Full Sync on subnet <-> vpn mapping change for subnet-id {} vpn-id {}", subnetId, vpnId); resubscribe(); } } @SuppressWarnings("unchecked") private synchronized <T extends DataObject> void addFullSyncModification(String listenerKey, T modification, ModificationType modificationType) { Collection<T> listenerModifications = (Collection<T>) fullSyncModifications.get(listenerKey); if (listenerModifications == null) { listenerModifications = new ArrayList<>(); fullSyncModifications.put(listenerKey, listenerModifications); } FederationPluginCounters.ingress_full_sync_modification.inc(); logger.trace("Add modification type {} listener {} data {}", modificationType, listenerKey, modification); listenerModifications.add(modification); } @SuppressWarnings("checkstyle:IllegalCatch") private void processFullSyncModifications(int generationNumber) throws FederationCorruptedStateException { for (String listenerKey : FederationPluginUtils.getOrderedListenerKeys()) { Collection<? extends DataObject> listenerModifications = fullSyncModifications.get(listenerKey); if (listenerModifications != null) { try { logger.debug("Start processing full sync for listener", listenerKey); processModifications(listenerKey, listenerModifications, ModificationType.WRITE, generationNumber); } catch (Exception e) { logger.error("Failed to process full sync for listener {}", listenerKey, e); throw e; } } } logger.info("Full sync process finished - generation number {} and remoteIp {}", generationNumber, remoteIp); FederationPluginCleaner.removeOldGenerationFederatedEntities(dataBroker, generationNumber, remoteIp); } private <T extends DataObject> void processModifications(String listenerKey, Collection<? extends DataObject> modifications, ModificationType modificationType, int generationNumber) throws FederationCorruptedStateException { attemptProcessModifications(listenerKey, modifications, modificationType, MAX_TRANSACTION_SUBMIT_RETRIES, generationNumber); } private void attemptProcessModifications(String listenerKey, Collection<? extends DataObject> modifications, ModificationType modificationType, int remainingRetries, int generationNumber) throws FederationCorruptedStateException { WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); for (DataObject modification : modifications) { processModification(listenerKey, modification, modificationType, tx, generationNumber); } try { tx.submit().checkedGet(); } catch (TransactionCommitFailedException e) { if (remainingRetries > 0) { logger.error("Process modification failed, retrying."); attemptProcessModifications(listenerKey, modifications, modificationType, --remainingRetries, generationNumber); } else { throw new FederationCorruptedStateException( "Failed to commit modification for listener " + listenerKey, e); } } } private <T extends DataObject, S extends DataObject> void processModification(String listenerKey, S modification, ModificationType modificationType, int generationNumber) throws FederationCorruptedStateException { processModification(listenerKey, modification, modificationType, null, generationNumber); } private <T extends DataObject, S extends DataObject> void processModification(String listenerKey, S modification, ModificationType modificationType, WriteTransaction tx, int generationNumber) throws FederationCorruptedStateException { FederationPluginCounters.ingress_process_modification.inc(); LogicalDatastoreType datastoreType = FederationPluginUtils.getListenerDatastoreType(listenerKey); if (datastoreType == null) { logger.error("Failed to get datastore type for {}", listenerKey); return; } if (!applyFilter(listenerKey, modification, modificationType)) { logger.trace("listener {} {} filtered out", listenerKey, modification); return; } Pair<InstanceIdentifier<T>, T> transformedModification = FederationPluginUtils.applyIngressTransformation( listenerKey, modification, modificationType, generationNumber, remoteIp); if (transformedModification == null) { logger.error("Failed to apply ingress transformation for {} {}", listenerKey, modification); return; } if (ModificationType.DELETE.equals(modificationType)) { logger.trace("Delete modification listener {} identifier {}", listenerKey, transformedModification.getKey()); deleteModification(datastoreType, transformedModification.getKey(), MAX_TRANSACTION_SUBMIT_RETRIES); return; } logger.trace("Write modification type {} listener {} data {}", modificationType, listenerKey, transformedModification); if (tx == null) { writeModification(datastoreType, transformedModification.getKey(), transformedModification.getValue(), MAX_TRANSACTION_SUBMIT_RETRIES); } else { writeModification(listenerKey, datastoreType, transformedModification.getKey(), transformedModification.getValue(), tx); } } private <R extends DataObject> boolean applyFilter(String listenerKey, R dataObject, ModificationType modificationType) { FilterResult filterResult = FederationPluginUtils.applyIngressFilter(listenerKey, dataObject); if (filterResult == null) { logger.warn("Failed to get FilterResult for {} {}", listenerKey, dataObject); return false; } logger.trace("{} filter result {}", listenerKey, filterResult); switch (filterResult) { case DENY: FederationPluginCounters.ingress_filter_result_deny.inc(); return false; case ACCEPT: FederationPluginCounters.ingress_filter_result_accept.inc(); return true; case QUEUE: FederationPluginCounters.ingress_filter_result_queue.inc(); logger.error("Ingress queue not supported"); return false; default: break; } return false; } // This is a workaround for bug https://bugs.opendaylight.org/show_bug.cgi?id=7420 @SuppressWarnings("checkstyle:emptyblock") private <T extends DataObject> void retryingMerge(LogicalDatastoreType datastoreType, InstanceIdentifier<T> instanceIdentifier, T dataObject, WriteTransaction tx, int remainingRetries) { try { tx.merge(datastoreType, instanceIdentifier, dataObject); } catch (UncheckedExecutionException t) { if (remainingRetries > 0) { logger.warn("Merge failed due to frozen class bug, sleeping and retrying", t); try { Thread.sleep(1500); } catch (InterruptedException e) { } retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, --remainingRetries); } } } private <T extends DataObject> void writeModification(LogicalDatastoreType datastoreType, InstanceIdentifier<T> instanceIdentifier, T dataObject, int remainingRetries) throws FederationCorruptedStateException { FederationPluginCounters.ingress_write_modification.inc(); WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, 1); try { tx.submit().checkedGet(); } catch (TransactionCommitFailedException e) { if (remainingRetries > 0) { writeModification(datastoreType, instanceIdentifier, dataObject, --remainingRetries); } else { throw new FederationCorruptedStateException( "Failed to write modification for " + instanceIdentifier.toString(), e); } } } private <T extends DataObject> void writeModification(String listenerKey, LogicalDatastoreType datastoreType, InstanceIdentifier<T> instanceIdentifier, T dataObject, WriteTransaction tx) { FederationPluginCounters.ingress_add_to_tx_modification.inc(); retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, 1); } private <T extends DataObject> void deleteModification(LogicalDatastoreType datastoreType, InstanceIdentifier<T> instanceIdentifier, int remainingRetries) throws FederationCorruptedStateException { FederationPluginCounters.ingress_delete_modification.inc(); WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); tx.delete(datastoreType, instanceIdentifier); try { tx.submit().checkedGet(); } catch (TransactionCommitFailedException e) { if (remainingRetries > 0) { deleteModification(datastoreType, instanceIdentifier, --remainingRetries); } else { throw new FederationCorruptedStateException( "Failed to delete modification for " + instanceIdentifier.toString(), e); } } } }