package com.thinkbiganalytics.nifi.provenance;
/*-
* #%L
* thinkbig-nifi-provenance-repo
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import com.thinkbiganalytics.metadata.rest.model.nifi.NiFiFlowCacheConnectionData;
import com.thinkbiganalytics.metadata.rest.model.nifi.NiFiFlowCacheSync;
import com.thinkbiganalytics.metadata.rest.model.nifi.NifiFlowCacheSnapshot;
import com.thinkbiganalytics.nifi.provenance.model.FeedFlowFile;
import com.thinkbiganalytics.nifi.provenance.model.ProvenanceEventRecordDTO;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class to lookup Kylo Feed information as it relates to a given Provenance Event.
* This uses the {@link NiFiFlowCacheSync} which holds data from Kylo about the Flows in NiFi.
*/
public class ProvenanceFeedLookup {
private static final Logger log = LoggerFactory.getLogger(ProvenanceFeedLookup.class);
/**
* The cache of Flow data
*/
NiFiFlowCacheSync flowCache;
/**
* Time when the cache was last updated
*/
private DateTime lastUpdated;
/**
* update this cache with the Kylo server
*
* @param updates the returned object from the KyloFlowProvider that has only those items that should be updated
*/
public void updateFlowCache(NiFiFlowCacheSync updates) {
if (updates.needsUpdate()) {
if (flowCache == null) {
flowCache = updates;
} else {
flowCache.getSnapshot().update(updates.getSnapshot());
}
lastUpdated = updates.getLastSync();
}
}
/**
* Returns the size of the Processors Found in Nifi that are cached
*
* @return the processors size mapped in the kylo cache
*/
public Integer getProcessorIdMapSize() {
return getFlowCache().getProcessorIdToFeedNameMap().size();
}
/**
* Returns the Cache object. if the cache is null it will return an Empty object NifiFlowCacheSnapshot.EMPTY
*
* @return the Cache in use or an NifiFlowCacheSnapshot.EMPTY cache object
*/
private NifiFlowCacheSnapshot getFlowCache() {
if (flowCache == null || flowCache.getSnapshot() == null) {
return NifiFlowCacheSnapshot.EMPTY;
} else {
return flowCache.getSnapshot();
}
}
/**
* Get the FeedName for a given processorId
*
* @param processorId the processorId to check
* @return the name associated with the processor
*/
private String getFeedName(String processorId) {
return getFlowCache().getProcessorIdToFeedNameMap().get(processorId);
}
/**
* Get the Feed ProcessorGroup for a given processorId
*
* @param processorId the processorId to check
* @return the feed process group id
*/
private String getFeedProcessGroupId(String processorId) {
return getFlowCache().getProcessorIdToFeedProcessGroupId().get(processorId);
}
/**
* Get the Processor Display name for a given processorId
*
* @param processorId the processorId to check
* @return the display name for the processor
*/
public String getProcessorName(String processorId) {
return getFlowCache().getProcessorIdToProcessorName().get(processorId);
}
public boolean isKyloManagedConnection(String connectionId) {
return getFlowCache().getConnectionIdToConnectionName().containsKey(connectionId);
}
/**
* Check to make sure the processorId is managed by Kylo and in the cache.
*
* @param processorId the processorId to check
* @return true if the processor is mapped to a Kylo managed feed flow, false if not
*/
public boolean isKyloManaged(String processorId) {
return StringUtils.isNotBlank(getProcessorName(processorId)) || isKyloManagedConnection(processorId);
}
/**
* For the given event, look at the connectionName to determine if the event should be treated as a Failure in Kylo.
* If the connection name has the word "failure" in it, this event will be marked as a Failure.
* If the connection name has the word "warn" in it, this event will be marked as a Warning.
* If the event is "Auto Terminated by Failure" it will be marked as a Failure.
*
* Connection data is maintained in the NifiFlowCache
*
* @param event the event to set/check
* @return the event processorType
*/
public KyloProcessorFlowType setProcessorFlowType(ProvenanceEventRecordDTO event) {
if (event.getProcessorType() == null) {
if (event.isTerminatedByFailureRelationship()) {
event.setProcessorType(KyloProcessorFlowType.FAILURE);
event.setIsFailure(true);
}
if (event.getSourceConnectionIdentifier() != null) {
NiFiFlowCacheConnectionData connectionData = getFlowCache().getConnectionIdToConnection().get(event.getSourceConnectionIdentifier());
if (connectionData != null && connectionData.getName() != null) {
if (connectionData.getName().toLowerCase().contains("failure")) {
event.setProcessorType(KyloProcessorFlowType.FAILURE);
event.setIsFailure(true);
//if this is a failure because of the connection name it means the previous event failed.
//todo is there a way to efficiently set the previous event as being failed
} else if (connectionData.getName().toLowerCase().contains("warn")) {
event.setProcessorType(KyloProcessorFlowType.WARNING);
}
}
if (event.getProcessorType() == null) {
event.setProcessorType(KyloProcessorFlowType.NORMAL_FLOW);
}
}
}
return event.getProcessorType();
}
/**
* Check to see if this event is a Failure event.
*
* @param eventRecordDTO the event to check
* @return true if it is a failed event, false if not
* @see this#setProcessorFlowType(ProvenanceEventRecordDTO)
*/
public boolean isFailureEvent(ProvenanceEventRecordDTO eventRecordDTO) {
KyloProcessorFlowType processorFlowType = eventRecordDTO.getProcessorType();
if (processorFlowType == null) {
processorFlowType = setProcessorFlowType(eventRecordDTO);
}
if (processorFlowType != null) {
return KyloProcessorFlowType.FAILURE.equals(processorFlowType);
} else {
return false;
}
}
/**
* Assign the Feed name and Feed ProcessGroup to the FeedFlowFile
*
* @param flowFile the feedFlowFile to check
* @return true if the feed information was found and assigned, false if not
*/
public boolean assignFeedInformationToFlowFile(FeedFlowFile flowFile) {
boolean assigned = flowFile.hasFeedInformationAssigned();
if (!assigned) {
String feedName = getFeedName(flowFile.getFirstEventProcessorId());
String processGroupId = getFeedProcessGroupId(flowFile.getFirstEventProcessorId());
flowFile.setFeedName(feedName);
flowFile.setFeedProcessGroupId(processGroupId);
assigned = true;
}
return assigned;
}
/**
* Check to see if this event is registered to a Template that is marked as being a Stream
*
* @param eventRecordDTO the event to check
* @return true if the event is part of a feed/template from Kylo indicated as a stream, false if not
*/
public boolean isStream(ProvenanceEventRecordDTO eventRecordDTO) {
return getFlowCache().getAllStreamingFeeds().contains(eventRecordDTO.getFeedName());
}
}