use of org.opensearch.ad.model.EntityProfileName in project anomaly-detection by opensearch-project.
the class EntityProfileRunner method validateEntity.
/**
* Verify if the input entity exists or not in case of typos.
*
* If a user deletes the entity after job start, then we will not be able to
* get this entity in the index. For this case, we will not return a profile
* for this entity even if it's running on some data node. the entity's model
* will be deleted by another entity or by maintenance due to long inactivity.
*
* @param entity Entity accessor
* @param categoryFields category fields defined for a detector
* @param detectorId Detector Id
* @param profilesToCollect Profile to collect from the input
* @param detector Detector config accessor
* @param listener Callback to send responses.
*/
private void validateEntity(Entity entity, List<String> categoryFields, String detectorId, Set<EntityProfileName> profilesToCollect, AnomalyDetector detector, ActionListener<EntityProfile> listener) {
Map<String, String> attributes = entity.getAttributes();
if (attributes == null || attributes.size() != categoryFields.size()) {
listener.onFailure(new IllegalArgumentException(EMPTY_ENTITY_ATTRIBUTES));
return;
}
for (String field : categoryFields) {
if (false == attributes.containsKey(field)) {
listener.onFailure(new IllegalArgumentException("Cannot find " + field));
return;
}
}
BoolQueryBuilder internalFilterQuery = QueryBuilders.boolQuery().filter(detector.getFilterQuery());
for (TermQueryBuilder term : entity.getTermQueryBuilders()) {
internalFilterQuery.filter(term);
}
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(internalFilterQuery).size(1);
SearchRequest searchRequest = new SearchRequest(detector.getIndices().toArray(new String[0]), searchSourceBuilder).preference(Preference.LOCAL.toString());
client.search(searchRequest, ActionListener.wrap(searchResponse -> {
try {
if (searchResponse.getHits().getHits().length == 0) {
listener.onFailure(new IllegalArgumentException(NO_ENTITY));
return;
}
prepareEntityProfile(listener, detectorId, entity, profilesToCollect, detector, categoryFields.get(0));
} catch (Exception e) {
listener.onFailure(new IllegalArgumentException(NO_ENTITY));
return;
}
}, e -> listener.onFailure(new IllegalArgumentException(NO_ENTITY))));
}
use of org.opensearch.ad.model.EntityProfileName in project anomaly-detection by opensearch-project.
the class EntityProfileRunner method prepareEntityProfile.
private void prepareEntityProfile(ActionListener<EntityProfile> listener, String detectorId, Entity entityValue, Set<EntityProfileName> profilesToCollect, AnomalyDetector detector, String categoryField) {
EntityProfileRequest request = new EntityProfileRequest(detectorId, entityValue, profilesToCollect);
client.execute(EntityProfileAction.INSTANCE, request, ActionListener.wrap(r -> getJob(detectorId, entityValue, profilesToCollect, detector, r, listener), listener::onFailure));
}
use of org.opensearch.ad.model.EntityProfileName in project anomaly-detection by opensearch-project.
the class EntityProfileTransportAction method doExecute.
@Override
protected void doExecute(Task task, EntityProfileRequest request, ActionListener<EntityProfileResponse> listener) {
String adID = request.getAdID();
Entity entityValue = request.getEntityValue();
Optional<String> modelIdOptional = entityValue.getModelId(adID);
if (false == modelIdOptional.isPresent()) {
listener.onFailure(new AnomalyDetectionException(adID, NO_MODEL_ID_FOUND_MSG));
return;
}
// we use entity's toString (e.g., app_0) to find its node
// This should be consistent with how we land a model node in AnomalyResultTransportAction
Optional<DiscoveryNode> node = hashRing.getOwningNodeWithSameLocalAdVersionForRealtimeAD(entityValue.toString());
if (false == node.isPresent()) {
listener.onFailure(new AnomalyDetectionException(adID, NO_NODE_FOUND_MSG));
return;
}
String nodeId = node.get().getId();
String modelId = modelIdOptional.get();
DiscoveryNode localNode = clusterService.localNode();
if (localNode.getId().equals(nodeId)) {
EntityCache cache = cacheProvider.get();
Set<EntityProfileName> profilesToCollect = request.getProfilesToCollect();
EntityProfileResponse.Builder builder = new EntityProfileResponse.Builder();
if (profilesToCollect.contains(EntityProfileName.ENTITY_INFO)) {
builder.setActive(cache.isActive(adID, modelId));
builder.setLastActiveMs(cache.getLastActiveMs(adID, modelId));
}
if (profilesToCollect.contains(EntityProfileName.INIT_PROGRESS) || profilesToCollect.contains(EntityProfileName.STATE)) {
builder.setTotalUpdates(cache.getTotalUpdates(adID, modelId));
}
if (profilesToCollect.contains(EntityProfileName.MODELS)) {
Optional<ModelProfile> modleProfile = cache.getModelProfile(adID, modelId);
if (modleProfile.isPresent()) {
builder.setModelProfile(new ModelProfileOnNode(nodeId, modleProfile.get()));
}
}
listener.onResponse(builder.build());
} else if (request.remoteAddress() == null) {
// redirect if request comes from local host.
// If a request comes from remote machine, it is already redirected.
// One redirection should be enough.
// We don't want a potential infinite loop due to any bug and thus give up.
LOG.info("Sending entity profile request to {} for detector {}, entity {}", nodeId, adID, entityValue);
try {
transportService.sendRequest(node.get(), EntityProfileAction.NAME, request, option, new TransportResponseHandler<EntityProfileResponse>() {
@Override
public EntityProfileResponse read(StreamInput in) throws IOException {
return new EntityProfileResponse(in);
}
@Override
public void handleResponse(EntityProfileResponse response) {
listener.onResponse(response);
}
@Override
public void handleException(TransportException exp) {
listener.onFailure(exp);
}
@Override
public String executor() {
return ThreadPool.Names.SAME;
}
});
} catch (Exception e) {
LOG.error(String.format(Locale.ROOT, "Fail to get entity profile for detector {}, entity {}", adID, entityValue), e);
listener.onFailure(new AnomalyDetectionException(adID, FAIL_TO_GET_ENTITY_PROFILE_MSG, e));
}
} else {
// Prior to Opensearch 1.1, we map a node using model id in the profile API.
// This is not consistent how we map node in AnomalyResultTransportAction, where
// we use entity values. We fixed that bug in Opensearch 1.1. But this can cause
// issue when a request involving an old node according to model id.
// The new node finds the entity value does not map to itself, so it redirects to another node.
// The redirection can cause an infinite loop. This branch breaks the loop and gives up.
LOG.error("Fail to get entity profile for detector {}, entity {}. Maybe because old and new node" + " are using different keys for the hash ring.", adID, entityValue);
listener.onFailure(new AnomalyDetectionException(adID, FAIL_TO_GET_ENTITY_PROFILE_MSG));
}
}
use of org.opensearch.ad.model.EntityProfileName in project anomaly-detection by opensearch-project.
the class EntityProfileRequest method writeTo.
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(adID);
if (Bwc.supportMultiCategoryFields(out.getVersion())) {
entityValue.writeTo(out);
} else {
// entity profile involving an old node won't work. Read
// EntityProfileTransportAction.doExecute for details. Write
// a string to not cause EOF exception.
out.writeString(entityValue.toString());
}
out.writeVInt(profilesToCollect.size());
for (EntityProfileName profile : profilesToCollect) {
out.writeEnum(profile);
}
}
use of org.opensearch.ad.model.EntityProfileName in project anomaly-detection by opensearch-project.
the class GetAnomalyDetectorTransportAction method getExecute.
protected void getExecute(GetAnomalyDetectorRequest request, ActionListener<GetAnomalyDetectorResponse> listener) {
String detectorID = request.getDetectorID();
String typesStr = request.getTypeStr();
String rawPath = request.getRawPath();
Entity entity = request.getEntity();
boolean all = request.isAll();
boolean returnJob = request.isReturnJob();
boolean returnTask = request.isReturnTask();
try {
if (!Strings.isEmpty(typesStr) || rawPath.endsWith(PROFILE) || rawPath.endsWith(PROFILE + "/")) {
if (entity != null) {
Set<EntityProfileName> entityProfilesToCollect = getEntityProfilesToCollect(typesStr, all);
EntityProfileRunner profileRunner = new EntityProfileRunner(client, xContentRegistry, AnomalyDetectorSettings.NUM_MIN_SAMPLES);
profileRunner.profile(detectorID, entity, entityProfilesToCollect, ActionListener.wrap(profile -> {
listener.onResponse(new GetAnomalyDetectorResponse(0, null, 0, 0, null, null, false, null, null, false, null, null, profile, true));
}, e -> listener.onFailure(e)));
} else {
Set<DetectorProfileName> profilesToCollect = getProfilesToCollect(typesStr, all);
AnomalyDetectorProfileRunner profileRunner = new AnomalyDetectorProfileRunner(client, xContentRegistry, nodeFilter, AnomalyDetectorSettings.NUM_MIN_SAMPLES, transportService, adTaskManager);
profileRunner.profile(detectorID, getProfileActionListener(listener), profilesToCollect);
}
} else {
if (returnTask) {
adTaskManager.getAndExecuteOnLatestADTasks(detectorID, null, null, ALL_DETECTOR_TASK_TYPES, (taskList) -> {
Optional<ADTask> realtimeAdTask = Optional.empty();
Optional<ADTask> historicalAdTask = Optional.empty();
if (taskList != null && taskList.size() > 0) {
Map<String, ADTask> adTasks = new HashMap<>();
List<ADTask> duplicateAdTasks = new ArrayList<>();
for (ADTask task : taskList) {
if (adTasks.containsKey(task.getTaskType())) {
LOG.info("Found duplicate latest task of detector {}, task id: {}, task type: {}", detectorID, task.getTaskType(), task.getTaskId());
duplicateAdTasks.add(task);
continue;
}
adTasks.put(task.getTaskType(), task);
}
if (duplicateAdTasks.size() > 0) {
adTaskManager.resetLatestFlagAsFalse(duplicateAdTasks);
}
if (adTasks.containsKey(ADTaskType.REALTIME_HC_DETECTOR.name())) {
realtimeAdTask = Optional.ofNullable(adTasks.get(ADTaskType.REALTIME_HC_DETECTOR.name()));
} else if (adTasks.containsKey(ADTaskType.REALTIME_SINGLE_ENTITY.name())) {
realtimeAdTask = Optional.ofNullable(adTasks.get(ADTaskType.REALTIME_SINGLE_ENTITY.name()));
}
if (adTasks.containsKey(ADTaskType.HISTORICAL_HC_DETECTOR.name())) {
historicalAdTask = Optional.ofNullable(adTasks.get(ADTaskType.HISTORICAL_HC_DETECTOR.name()));
} else if (adTasks.containsKey(ADTaskType.HISTORICAL_SINGLE_ENTITY.name())) {
historicalAdTask = Optional.ofNullable(adTasks.get(ADTaskType.HISTORICAL_SINGLE_ENTITY.name()));
} else if (adTasks.containsKey(ADTaskType.HISTORICAL.name())) {
historicalAdTask = Optional.ofNullable(adTasks.get(ADTaskType.HISTORICAL.name()));
}
}
getDetectorAndJob(detectorID, returnJob, returnTask, realtimeAdTask, historicalAdTask, listener);
}, transportService, true, 2, listener);
} else {
getDetectorAndJob(detectorID, returnJob, returnTask, Optional.empty(), Optional.empty(), listener);
}
}
} catch (Exception e) {
LOG.error(e);
listener.onFailure(e);
}
}
Aggregations