diff --git a/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java b/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java index 91e34ab97..d61315edc 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java +++ b/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java @@ -367,21 +367,30 @@ public void generateRecommendations(KruizeObject kruizeObject) { } } - private void generateRecommendationsBasedOnNamespace(NamespaceData namespaceData, KruizeObject kruizeObject) { - Timestamp monitoringEndTime = namespaceData.getResults().keySet().stream().max(Timestamp::compareTo).get(); - NamespaceRecommendations namespaceRecommendations = namespaceData.getNamespaceRecommendations(); - if (null == namespaceRecommendations) { - namespaceRecommendations = new NamespaceRecommendations(); + private void generateRecommendationsBasedOnContainer(ContainerData containerData, KruizeObject kruizeObject) { + + // Get the monitoringEndTime from ResultData's ContainerData. Should have only one element + Timestamp monitoringEndTime = containerData.getResults().keySet().stream().max(Timestamp::compareTo).get(); + + ContainerRecommendations containerRecommendations = containerData.getContainerRecommendations(); + // Just to make sure the container recommendations object is not empty + if (null == containerRecommendations) { + containerRecommendations = new ContainerRecommendations(); } - HashMap recommendationLevelNM = namespaceRecommendations.getNotificationMap(); + + HashMap recommendationLevelNM = containerRecommendations.getNotificationMap(); if (null == recommendationLevelNM) { recommendationLevelNM = new HashMap<>(); } - HashMap timestampBasedRecommendationMap = namespaceRecommendations.getData(); + + // Get the engine recommendation map for a time stamp if it exists else create one + HashMap timestampBasedRecommendationMap + = containerRecommendations.getData(); if (null == timestampBasedRecommendationMap) { timestampBasedRecommendationMap = new HashMap<>(); } + // check if engines map exists else create one MappedRecommendationForTimestamp timestampRecommendation; if (timestampBasedRecommendationMap.containsKey(monitoringEndTime)) { timestampRecommendation = timestampBasedRecommendationMap.get(monitoringEndTime); @@ -391,14 +400,20 @@ private void generateRecommendationsBasedOnNamespace(NamespaceData namespaceData timestampRecommendation.setMonitoringEndTime(monitoringEndTime); - HashMap> currentConfig = getCurrentNamespaceConfigData(namespaceData, monitoringEndTime, timestampRecommendation); + // get the current config data + HashMap> currentConfig = getCurrentConfigData(containerData, monitoringEndTime, + timestampRecommendation); timestampRecommendation.setCurrentConfig(currentConfig); - boolean recommendationAvailable = generateNamespaceRecommendationsBasedOnTerms(namespaceData, kruizeObject, monitoringEndTime, currentConfig, timestampRecommendation); + // get recommendations based on terms + boolean recommendationAvailable = generateRecommendationsBasedOnTerms(containerData, kruizeObject, monitoringEndTime, currentConfig, timestampRecommendation); RecommendationNotification recommendationsLevelNotifications; if (recommendationAvailable) { + // put recommendations tagging to timestamp timestampBasedRecommendationMap.put(monitoringEndTime, timestampRecommendation); + // set the Recommendations object level notifications recommendationsLevelNotifications = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_RECOMMENDATIONS_AVAILABLE); } else { recommendationsLevelNotifications = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); @@ -406,64 +421,91 @@ private void generateRecommendationsBasedOnNamespace(NamespaceData namespaceData } recommendationLevelNM.put(recommendationsLevelNotifications.getCode(), recommendationsLevelNotifications); - namespaceRecommendations.setNotificationMap(recommendationLevelNM); - namespaceRecommendations.setData(timestampBasedRecommendationMap); - namespaceData.setNamespaceRecommendations(namespaceRecommendations); + containerRecommendations.setNotificationMap(recommendationLevelNM); + // set the data object to map + containerRecommendations.setData(timestampBasedRecommendationMap); + // set the container recommendations in container object + containerData.setContainerRecommendations(containerRecommendations); } - private HashMap> getCurrentNamespaceConfigData(NamespaceData namespaceData, Timestamp monitoringEndTime, MappedRecommendationForTimestamp timestampRecommendation) { + private HashMap> getCurrentConfigData(ContainerData containerData, Timestamp monitoringEndTime, + MappedRecommendationForTimestamp timestampRecommendation) { - HashMap> currentConfig = new HashMap<>(); + HashMap> currentConfig = new HashMap<>(); ArrayList notifications = new ArrayList<>(); + + // Create Current Requests Map HashMap currentRequestsMap = new HashMap<>(); + // Create Current Limits Map HashMap currentLimitsMap = new HashMap<>(); for (AnalyzerConstants.ResourceSetting resourceSetting : AnalyzerConstants.ResourceSetting.values()) { for (AnalyzerConstants.RecommendationItem recommendationItem : AnalyzerConstants.RecommendationItem.values()) { - RecommendationConfigItem configItem = RecommendationUtils.getCurrentValueForNamespace(namespaceData.getResults(), monitoringEndTime, resourceSetting, recommendationItem, notifications); + RecommendationConfigItem configItem = RecommendationUtils.getCurrentValue(containerData.getResults(), + monitoringEndTime, + resourceSetting, + recommendationItem, + notifications); - if (null == configItem) { + if (null == configItem) continue; - } if (null == configItem.getAmount()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_AMOUNT_MISSING_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_CPU_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_AMOUNT_MISSING_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_MEMORY_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } continue; } if (null == configItem.getFormat()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_FORMAT_MISSING_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_CPU_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_FORMAT_MISSING_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_MEMORY_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } continue; } if (configItem.getAmount() <= 0.0) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_AMOUNT_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_CPU_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_AMOUNT_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_MEMORY_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } continue; } if (configItem.getFormat().isEmpty() || configItem.getFormat().isBlank()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_FORMAT_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_CPU_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_FORMAT_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_MEMORY_SECTION + .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, + experimentName, interval_end_time))); } continue; } @@ -477,33 +519,43 @@ private HashMap> currentConfig, MappedRecommendationForTimestamp timestampRecommendation) { + private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, KruizeObject kruizeObject, + Timestamp monitoringEndTime, + HashMap> currentConfig, + MappedRecommendationForTimestamp timestampRecommendation) { + boolean recommendationAvailable = false; double measurementDuration = kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble(); for (Map.Entry termsEntry : kruizeObject.getTerms().entrySet()) { String recommendationTerm = termsEntry.getKey(); Terms terms = termsEntry.getValue(); - LOGGER.info("Namespace RecommendationTerm = {}", recommendationTerm); + LOGGER.debug("recommendationTerm = {}", recommendationTerm); int duration = termsEntry.getValue().getDays(); Timestamp monitoringStartTime = Terms.getMonitoringStartTime(monitoringEndTime, duration); - LOGGER.info("Namespace monitoringStartTime = {}", monitoringStartTime); + LOGGER.debug("monitoringStartTime = {}", monitoringStartTime); TermRecommendations mappedRecommendationForTerm = new TermRecommendations(); // Check if there is min data available for the term - if (!Terms.checkIfMinDataAvailableForTermNamespace(namespaceData, terms, monitoringEndTime, measurementDuration)) { - RecommendationNotification recommendationNotification = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); + if (!Terms.checkIfMinDataAvailableForTerm(containerData, terms, monitoringEndTime, measurementDuration)) { + RecommendationNotification recommendationNotification = new RecommendationNotification( + RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); mappedRecommendationForTerm.addNotification(recommendationNotification); } else { ArrayList termLevelNotifications = new ArrayList<>(); @@ -519,10 +571,10 @@ private boolean generateNamespaceRecommendationsBasedOnTerms(NamespaceData names } // Now generate a new recommendation for the new data corresponding to the monitoringEndTime - MappedRecommendationForModel mappedRecommendationForModel = generateNamespaceRecommendationBasedOnModel( + MappedRecommendationForModel mappedRecommendationForModel = generateRecommendationBasedOnModel( monitoringStartTime, model, - namespaceData, + containerData, monitoringEndTime, kruizeObject.getRecommendation_settings(), currentConfig, @@ -570,9 +622,26 @@ private boolean generateNamespaceRecommendationsBasedOnTerms(NamespaceData names mappedRecommendationForTerm.addNotification(recommendationNotification); } mappedRecommendationForTerm.setMonitoringStartTime(monitoringStartTime); - + // generate plots when minimum data is available for the term + if (KruizeDeploymentInfo.plots) { + if (null != monitoringStartTime) { + Timer.Sample timerBoxPlots = null; + String status = "success"; // TODO avoid this constant at multiple place + try { + timerBoxPlots = Timer.start(MetricsConfig.meterRegistry()); + mappedRecommendationForTerm.setPlots(new PlotManager(containerData.getResults(), terms, monitoringStartTime, monitoringEndTime).generatePlots()); + } catch (Exception e) { + status = String.format("Box plots Failed due to - %s", e.getMessage()); + } finally { + if (timerBoxPlots != null) { + MetricsConfig.timerBoxPlots = MetricsConfig.timerBBoxPlots.tag("status", status).register(MetricsConfig.meterRegistry()); + timerBoxPlots.stop(MetricsConfig.timerBoxPlots); + } + } + } + } } - Terms.setDurationBasedOnTermNamespace(namespaceData, mappedRecommendationForTerm, recommendationTerm); + Terms.setDurationBasedOnTerm(containerData, mappedRecommendationForTerm, recommendationTerm); timestampRecommendation.setRecommendationForTermHashMap(recommendationTerm, mappedRecommendationForTerm); } @@ -580,7 +649,14 @@ private boolean generateNamespaceRecommendationsBasedOnTerms(NamespaceData names } - private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel(Timestamp monitoringStartTime, RecommendationModel model, NamespaceData namespaceData, Timestamp monitoringEndTime, RecommendationSettings recommendationSettings, HashMap> currentConfigMap, Map.Entry termEntry) { + private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestamp monitoringStartTime, RecommendationModel model, ContainerData containerData, + Timestamp monitoringEndTime, + RecommendationSettings recommendationSettings, + HashMap> currentConfigMap, + Map.Entry termEntry) { + MappedRecommendationForModel mappedRecommendationForModel = new MappedRecommendationForModel(); // Set CPU threshold to default double cpuThreshold = DEFAULT_CPU_THRESHOLD; @@ -614,7 +690,6 @@ private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel currentMemRequest = requestsMap.get(AnalyzerConstants.RecommendationItem.memory); } } - LOGGER.info("CURRENT REQ CPU " + currentCPURequest + "MEM: " + currentMemRequest); if (currentConfigMap.containsKey(AnalyzerConstants.ResourceSetting.limits) && null != currentConfigMap.get(AnalyzerConstants.ResourceSetting.limits)) { HashMap limitsMap = currentConfigMap.get(AnalyzerConstants.ResourceSetting.limits); if (limitsMap.containsKey(AnalyzerConstants.RecommendationItem.cpu) && null != limitsMap.get(AnalyzerConstants.RecommendationItem.cpu)) { @@ -626,13 +701,13 @@ private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel } if (null != monitoringStartTime) { Timestamp finalMonitoringStartTime = monitoringStartTime; - Map filteredResultsMap = namespaceData.getResults().entrySet().stream() + Map filteredResultsMap = containerData.getResults().entrySet().stream() .filter((x -> ((x.getKey().compareTo(finalMonitoringStartTime) >= 0) && (x.getKey().compareTo(monitoringEndTime) <= 0)))) .collect((Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); // Set number of pods - int numPods = getNumPodsForNamespace(filteredResultsMap); + int numPods = getNumPods(filteredResultsMap); mappedRecommendationForModel.setPodsCount(numPods); @@ -640,8 +715,8 @@ private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel ArrayList notifications = new ArrayList<>(); // Get the Recommendation Items - RecommendationConfigItem recommendationCpuRequest = model.getCPURequestRecommendationForNamespace(filteredResultsMap, notifications); - RecommendationConfigItem recommendationMemRequest = model.getMemoryRequestRecommendationForNamespace(filteredResultsMap, notifications); + RecommendationConfigItem recommendationCpuRequest = model.getCPURequestRecommendation(filteredResultsMap, notifications); + RecommendationConfigItem recommendationMemRequest = model.getMemoryRequestRecommendation(filteredResultsMap, notifications); // Get the Recommendation Items // Calling requests on limits as we are maintaining limits and requests as same @@ -662,6 +737,8 @@ private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_CPU_LIMIT, recommendationCpuLimits); internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_MEMORY_REQUEST, recommendationMemRequest); internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_MEMORY_LIMIT, recommendationMemLimits); + + // Call the populate method to validate and populate the recommendation object boolean isSuccess = populateRecommendation( termEntry, @@ -680,30 +757,21 @@ private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel return mappedRecommendationForModel; } - private void generateRecommendationsBasedOnContainer(ContainerData containerData, KruizeObject kruizeObject) { - - // Get the monitoringEndTime from ResultData's ContainerData. Should have only one element - Timestamp monitoringEndTime = containerData.getResults().keySet().stream().max(Timestamp::compareTo).get(); - - ContainerRecommendations containerRecommendations = containerData.getContainerRecommendations(); - // Just to make sure the container recommendations object is not empty - if (null == containerRecommendations) { - containerRecommendations = new ContainerRecommendations(); + private void generateRecommendationsBasedOnNamespace(NamespaceData namespaceData, KruizeObject kruizeObject) { + Timestamp monitoringEndTime = namespaceData.getResults().keySet().stream().max(Timestamp::compareTo).get(); + NamespaceRecommendations namespaceRecommendations = namespaceData.getNamespaceRecommendations(); + if (null == namespaceRecommendations) { + namespaceRecommendations = new NamespaceRecommendations(); } - - HashMap recommendationLevelNM = containerRecommendations.getNotificationMap(); + HashMap recommendationLevelNM = namespaceRecommendations.getNotificationMap(); if (null == recommendationLevelNM) { recommendationLevelNM = new HashMap<>(); } - - // Get the engine recommendation map for a time stamp if it exists else create one - HashMap timestampBasedRecommendationMap - = containerRecommendations.getData(); + HashMap timestampBasedRecommendationMap = namespaceRecommendations.getData(); if (null == timestampBasedRecommendationMap) { timestampBasedRecommendationMap = new HashMap<>(); } - // check if engines map exists else create one MappedRecommendationForTimestamp timestampRecommendation; if (timestampBasedRecommendationMap.containsKey(monitoringEndTime)) { timestampRecommendation = timestampBasedRecommendationMap.get(monitoringEndTime); @@ -713,20 +781,14 @@ private void generateRecommendationsBasedOnContainer(ContainerData containerData timestampRecommendation.setMonitoringEndTime(monitoringEndTime); - // get the current config data - HashMap> currentConfig = getCurrentConfigData(containerData, monitoringEndTime, - timestampRecommendation); + HashMap> currentConfig = getCurrentNamespaceConfigData(namespaceData, monitoringEndTime, timestampRecommendation); timestampRecommendation.setCurrentConfig(currentConfig); - // get recommendations based on terms - boolean recommendationAvailable = generateRecommendationsBasedOnTerms(containerData, kruizeObject, monitoringEndTime, currentConfig, timestampRecommendation); + boolean recommendationAvailable = generateNamespaceRecommendationsBasedOnTerms(namespaceData, kruizeObject, monitoringEndTime, currentConfig, timestampRecommendation); RecommendationNotification recommendationsLevelNotifications; if (recommendationAvailable) { - // put recommendations tagging to timestamp timestampBasedRecommendationMap.put(monitoringEndTime, timestampRecommendation); - // set the Recommendations object level notifications recommendationsLevelNotifications = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_RECOMMENDATIONS_AVAILABLE); } else { recommendationsLevelNotifications = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); @@ -734,91 +796,64 @@ private void generateRecommendationsBasedOnContainer(ContainerData containerData } recommendationLevelNM.put(recommendationsLevelNotifications.getCode(), recommendationsLevelNotifications); - containerRecommendations.setNotificationMap(recommendationLevelNM); - // set the data object to map - containerRecommendations.setData(timestampBasedRecommendationMap); - // set the container recommendations in container object - containerData.setContainerRecommendations(containerRecommendations); + namespaceRecommendations.setNotificationMap(recommendationLevelNM); + namespaceRecommendations.setData(timestampBasedRecommendationMap); + namespaceData.setNamespaceRecommendations(namespaceRecommendations); } - private HashMap> getCurrentConfigData(ContainerData containerData, Timestamp monitoringEndTime, - MappedRecommendationForTimestamp timestampRecommendation) { + private HashMap> getCurrentNamespaceConfigData(NamespaceData namespaceData, Timestamp monitoringEndTime, MappedRecommendationForTimestamp timestampRecommendation) { - HashMap> currentConfig = new HashMap<>(); + HashMap> currentConfig = new HashMap<>(); ArrayList notifications = new ArrayList<>(); - - // Create Current Requests Map HashMap currentRequestsMap = new HashMap<>(); - // Create Current Limits Map HashMap currentLimitsMap = new HashMap<>(); for (AnalyzerConstants.ResourceSetting resourceSetting : AnalyzerConstants.ResourceSetting.values()) { for (AnalyzerConstants.RecommendationItem recommendationItem : AnalyzerConstants.RecommendationItem.values()) { - RecommendationConfigItem configItem = RecommendationUtils.getCurrentValue(containerData.getResults(), - monitoringEndTime, - resourceSetting, - recommendationItem, - notifications); + RecommendationConfigItem configItem = RecommendationUtils.getCurrentValueForNamespace(namespaceData.getResults(), monitoringEndTime, resourceSetting, recommendationItem, notifications); - if (null == configItem) + if (null == configItem) { continue; + } if (null == configItem.getAmount()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_AMOUNT_MISSING_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_CPU_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_AMOUNT_MISSING_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_MEMORY_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.AMOUNT_MISSING_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } continue; } if (null == configItem.getFormat()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_FORMAT_MISSING_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_CPU_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_FORMAT_MISSING_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_MEMORY_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.FORMAT_MISSING_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } continue; } if (configItem.getAmount() <= 0.0) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_AMOUNT_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_CPU_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_AMOUNT_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_MEMORY_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_AMOUNT_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } continue; } if (configItem.getFormat().isEmpty() || configItem.getFormat().isBlank()) { if (recommendationItem.equals(AnalyzerConstants.RecommendationItem.cpu)) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_FORMAT_IN_CPU_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_CPU_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_CPU_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } else if (recommendationItem.equals((AnalyzerConstants.RecommendationItem.memory))) { notifications.add(RecommendationConstants.RecommendationNotification.ERROR_INVALID_FORMAT_IN_MEMORY_SECTION); - LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_MEMORY_SECTION - .concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, - experimentName, interval_end_time))); + LOGGER.error(RecommendationConstants.RecommendationNotificationMsgConstant.INVALID_FORMAT_IN_MEMORY_SECTION.concat(String.format(AnalyzerErrorConstants.AutotuneObjectErrors.EXPERIMENT_AND_INTERVAL_END_TIME, experimentName, interval_end_time))); } continue; } @@ -832,43 +867,33 @@ RecommendationConfigItem>> getCurrentConfigData(ContainerData containerData, Tim } } - // Iterate over notifications and set to recommendations for (RecommendationConstants.RecommendationNotification recommendationNotification : notifications) { timestampRecommendation.addNotification(new RecommendationNotification(recommendationNotification)); } - // Check if map is not empty and set requests map to current config if (!currentRequestsMap.isEmpty()) { currentConfig.put(AnalyzerConstants.ResourceSetting.requests, currentRequestsMap); } - // Check if map is not empty and set limits map to current config if (!currentLimitsMap.isEmpty()) { currentConfig.put(AnalyzerConstants.ResourceSetting.limits, currentLimitsMap); } return currentConfig; } - private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, KruizeObject kruizeObject, - Timestamp monitoringEndTime, - HashMap> currentConfig, - MappedRecommendationForTimestamp timestampRecommendation) { - + private boolean generateNamespaceRecommendationsBasedOnTerms(NamespaceData namespaceData, KruizeObject kruizeObject, Timestamp monitoringEndTime, HashMap> currentConfig, MappedRecommendationForTimestamp timestampRecommendation) { boolean recommendationAvailable = false; double measurementDuration = kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble(); for (Map.Entry termsEntry : kruizeObject.getTerms().entrySet()) { String recommendationTerm = termsEntry.getKey(); Terms terms = termsEntry.getValue(); - LOGGER.debug("recommendationTerm = {}", recommendationTerm); + LOGGER.info("Namespace RecommendationTerm = {}", recommendationTerm); int duration = termsEntry.getValue().getDays(); Timestamp monitoringStartTime = Terms.getMonitoringStartTime(monitoringEndTime, duration); - LOGGER.debug("monitoringStartTime = {}", monitoringStartTime); + LOGGER.info("Namespace monitoringStartTime = {}", monitoringStartTime); TermRecommendations mappedRecommendationForTerm = new TermRecommendations(); // Check if there is min data available for the term - if (!Terms.checkIfMinDataAvailableForTerm(containerData, terms, monitoringEndTime, measurementDuration)) { - RecommendationNotification recommendationNotification = new RecommendationNotification( - RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); + if (!Terms.checkIfMinDataAvailableForTermNamespace(namespaceData, terms, monitoringEndTime, measurementDuration)) { + RecommendationNotification recommendationNotification = new RecommendationNotification(RecommendationConstants.RecommendationNotification.INFO_NOT_ENOUGH_DATA); mappedRecommendationForTerm.addNotification(recommendationNotification); } else { ArrayList termLevelNotifications = new ArrayList<>(); @@ -884,10 +909,10 @@ private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, } // Now generate a new recommendation for the new data corresponding to the monitoringEndTime - MappedRecommendationForModel mappedRecommendationForModel = generateRecommendationBasedOnModel( + MappedRecommendationForModel mappedRecommendationForModel = generateNamespaceRecommendationBasedOnModel( monitoringStartTime, model, - containerData, + namespaceData, monitoringEndTime, kruizeObject.getRecommendation_settings(), currentConfig, @@ -935,26 +960,9 @@ private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, mappedRecommendationForTerm.addNotification(recommendationNotification); } mappedRecommendationForTerm.setMonitoringStartTime(monitoringStartTime); - // generate plots when minimum data is available for the term - if (KruizeDeploymentInfo.plots) { - if (null != monitoringStartTime) { - Timer.Sample timerBoxPlots = null; - String status = "success"; // TODO avoid this constant at multiple place - try { - timerBoxPlots = Timer.start(MetricsConfig.meterRegistry()); - mappedRecommendationForTerm.setPlots(new PlotManager(containerData.getResults(), terms, monitoringStartTime, monitoringEndTime).generatePlots()); - } catch (Exception e) { - status = String.format("Box plots Failed due to - %s", e.getMessage()); - } finally { - if (timerBoxPlots != null) { - MetricsConfig.timerBoxPlots = MetricsConfig.timerBBoxPlots.tag("status", status).register(MetricsConfig.meterRegistry()); - timerBoxPlots.stop(MetricsConfig.timerBoxPlots); - } - } - } - } + } - Terms.setDurationBasedOnTerm(containerData, mappedRecommendationForTerm, recommendationTerm); + Terms.setDurationBasedOnTermNamespace(namespaceData, mappedRecommendationForTerm, recommendationTerm); timestampRecommendation.setRecommendationForTermHashMap(recommendationTerm, mappedRecommendationForTerm); } @@ -962,14 +970,7 @@ private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, } - private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestamp monitoringStartTime, RecommendationModel model, ContainerData containerData, - Timestamp monitoringEndTime, - RecommendationSettings recommendationSettings, - HashMap> currentConfigMap, - Map.Entry termEntry) { - + private MappedRecommendationForModel generateNamespaceRecommendationBasedOnModel(Timestamp monitoringStartTime, RecommendationModel model, NamespaceData namespaceData, Timestamp monitoringEndTime, RecommendationSettings recommendationSettings, HashMap> currentConfigMap, Map.Entry termEntry) { MappedRecommendationForModel mappedRecommendationForModel = new MappedRecommendationForModel(); // Set CPU threshold to default double cpuThreshold = DEFAULT_CPU_THRESHOLD; @@ -1003,6 +1004,7 @@ private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestam currentMemRequest = requestsMap.get(AnalyzerConstants.RecommendationItem.memory); } } + LOGGER.info("CURRENT REQ CPU " + currentCPURequest + "MEM: " + currentMemRequest); if (currentConfigMap.containsKey(AnalyzerConstants.ResourceSetting.limits) && null != currentConfigMap.get(AnalyzerConstants.ResourceSetting.limits)) { HashMap limitsMap = currentConfigMap.get(AnalyzerConstants.ResourceSetting.limits); if (limitsMap.containsKey(AnalyzerConstants.RecommendationItem.cpu) && null != limitsMap.get(AnalyzerConstants.RecommendationItem.cpu)) { @@ -1014,13 +1016,13 @@ private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestam } if (null != monitoringStartTime) { Timestamp finalMonitoringStartTime = monitoringStartTime; - Map filteredResultsMap = containerData.getResults().entrySet().stream() + Map filteredResultsMap = namespaceData.getResults().entrySet().stream() .filter((x -> ((x.getKey().compareTo(finalMonitoringStartTime) >= 0) && (x.getKey().compareTo(monitoringEndTime) <= 0)))) .collect((Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); // Set number of pods - int numPods = getNumPods(filteredResultsMap); + int numPods = getNumPodsForNamespace(filteredResultsMap); mappedRecommendationForModel.setPodsCount(numPods); @@ -1028,8 +1030,8 @@ private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestam ArrayList notifications = new ArrayList<>(); // Get the Recommendation Items - RecommendationConfigItem recommendationCpuRequest = model.getCPURequestRecommendation(filteredResultsMap, notifications); - RecommendationConfigItem recommendationMemRequest = model.getMemoryRequestRecommendation(filteredResultsMap, notifications); + RecommendationConfigItem recommendationCpuRequest = model.getCPURequestRecommendationForNamespace(filteredResultsMap, notifications); + RecommendationConfigItem recommendationMemRequest = model.getMemoryRequestRecommendationForNamespace(filteredResultsMap, notifications); // Get the Recommendation Items // Calling requests on limits as we are maintaining limits and requests as same @@ -1050,8 +1052,6 @@ private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestam internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_CPU_LIMIT, recommendationCpuLimits); internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_MEMORY_REQUEST, recommendationMemRequest); internalMapToPopulate.put(RecommendationConstants.RecommendationEngine.InternalConstants.RECOMMENDED_MEMORY_LIMIT, recommendationMemLimits); - - // Call the populate method to validate and populate the recommendation object boolean isSuccess = populateRecommendation( termEntry, @@ -1070,6 +1070,8 @@ private MappedRecommendationForModel generateRecommendationBasedOnModel(Timestam return mappedRecommendationForModel; } + + /** * This method handles validating the data and populating to the recommendation object *

@@ -1788,150 +1790,6 @@ public void fetchMetricsBasedOnDatasource(KruizeObject kruizeObject, Timestamp i // Iterate over Kubernetes objects for (K8sObject k8sObject : kubernetes_objects) { String namespace = k8sObject.getNamespace(); - NamespaceData namespaceData = k8sObject.getNamespaceData(); - - // fetch namespace related metrics - if (null == interval_end_time) { - LOGGER.info("Determine the date of the last activity for the namespace based on container usage."); - String dateMetricsUrl = String.format(KruizeConstants.DataSourceConstants.DATE_ENDPOINT_WITH_QUERY, - dataSourceInfo.getUrl(), - URLEncoder.encode(String.format(PromQLDataSourceQueries.NAMESPACE_MAX_DATE, namespace), CHARACTER_ENCODING) - ); - LOGGER.info(dateMetricsUrl); - JSONObject genericJsonObject = new GenericRestApiClient(dateMetricsUrl).fetchMetricsJson("get", ""); - JsonObject jsonObject = new Gson().fromJson(genericJsonObject.toString(), JsonObject.class); - JsonArray resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray(KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT); - // Process fetched metrics - if (null != resultArray && !resultArray.isEmpty()) { - resultArray = resultArray.get(0).getAsJsonObject().getAsJsonArray("value"); - long epochTime = resultArray.get(0).getAsLong(); - String timestamp = sdf.format(new Date(epochTime * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC)); - Date date = sdf.parse(timestamp); - Timestamp dateTS = new Timestamp(date.getTime()); - interval_end_time_epoc = dateTS.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - ((long) dateTS.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); - int maxDay = Terms.getMaxDays(kruizeObject.getTerms()); - LOGGER.info("maxDay : {}", maxDay); - Timestamp startDateTS = Timestamp.valueOf(Objects.requireNonNull(dateTS).toLocalDateTime().minusDays(maxDay)); - interval_start_time_epoc = startDateTS.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - ((long) startDateTS.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC); - } - } else { - // Convert timestamps to epoch time - interval_end_time_epoc = interval_end_time.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - - ((long) interval_end_time.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); - interval_start_time_epoc = interval_start_time.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - - ((long) interval_start_time.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC); - } - - HashMap namespaceDataResults = new HashMap<>(); - IntervalResults namespaceIntervalResults; - HashMap namespaceResMap; - MetricResults namespaceMetricResults; - MetricAggregationInfoResults namespaceMetricAggregationInfoResults; - if (null == namespaceData) { - namespaceData = new NamespaceData(); - namespaceData.setNamespace_name(namespace); - k8sObject.setNamespaceData(namespaceData); - } - // Iterate over metrics and aggregation methods - for (Map.Entry metricEntry : promQls.entrySet()) { - if (metricEntry.getKey().name().startsWith("namespace")) { - for (String methodName : aggregationMethods) { - String promQL = null; - String format = null; - // Determine promQL and format based on metric type - if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuUsage || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuThrottle) && !methodName.equals(KruizeConstants.JSONKeys.SUM)) { - promQL = String.format(metricEntry.getValue(), methodName, namespace, measurementDurationMinutesInDouble.intValue()); - format = KruizeConstants.JSONKeys.CORES; - } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuLimit || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuRequest) && methodName.equals(KruizeConstants.JSONKeys.SUM)) { - promQL = String.format(metricEntry.getValue(), methodName, namespace); - format = KruizeConstants.JSONKeys.CORES; - } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryUsage || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryRSS) && !methodName.equals(KruizeConstants.JSONKeys.SUM)) { - promQL = String.format(metricEntry.getValue(), methodName, namespace, measurementDurationMinutesInDouble.intValue()); - format = KruizeConstants.JSONKeys.BYTES; - } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryLimit || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryRequest) && methodName.equals(KruizeConstants.JSONKeys.SUM)) { - promQL = String.format(metricEntry.getValue(), methodName, namespace); - format = KruizeConstants.JSONKeys.BYTES; - } - LOGGER.info("Metric Name: " + metricEntry.getKey() + " Method Name: " + methodName); - // If promQL is determined, fetch metrics from the datasource - if (promQL != null) { - LOGGER.info(promQL); - String namespaceMetricsUrl; - try { - namespaceMetricsUrl = String.format(KruizeConstants.DataSourceConstants.DATASOURCE_ENDPOINT_WITH_QUERY, - dataSourceInfo.getUrl(), - URLEncoder.encode(promQL, CHARACTER_ENCODING), - interval_start_time_epoc, - interval_end_time_epoc, - measurementDurationMinutesInDouble.intValue() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); - LOGGER.info(namespaceMetricsUrl); - JSONObject genericJsonObject = new GenericRestApiClient(namespaceMetricsUrl).fetchMetricsJson("get", ""); - JsonObject jsonObject = new Gson().fromJson(genericJsonObject.toString(), JsonObject.class); - JsonArray resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray(KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT); - // Process fetched metrics - if (null != resultArray && !resultArray.isEmpty()) { - resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray( - KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT).get(0) - .getAsJsonObject().getAsJsonArray(KruizeConstants.DataSourceConstants - .DataSourceQueryJSONKeys.VALUES); - sdf.setTimeZone(TimeZone.getTimeZone(KruizeConstants.TimeUnitsExt.TimeZones.UTC)); - - // Iterate over fetched metrics - Timestamp sTime = new Timestamp(interval_start_time_epoc); - for (JsonElement element : resultArray) { - JsonArray valueArray = element.getAsJsonArray(); - long epochTime = valueArray.get(0).getAsLong(); - double value = valueArray.get(1).getAsDouble(); - String timestamp = sdf.format(new Date(epochTime * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC)); - Date date = sdf.parse(timestamp); - Timestamp eTime = new Timestamp(date.getTime()); - - // Prepare interval results - if (namespaceDataResults.containsKey(eTime)) { - namespaceIntervalResults = namespaceDataResults.get(eTime); - namespaceResMap = namespaceIntervalResults.getMetricResultsMap(); - } else { - namespaceIntervalResults = new IntervalResults(); - namespaceResMap = new HashMap<>(); - } - if (namespaceResMap.containsKey(metricEntry.getKey())) { - namespaceMetricResults = namespaceResMap.get(metricEntry.getKey()); - namespaceMetricAggregationInfoResults = namespaceMetricResults.getAggregationInfoResult(); - } else { - namespaceMetricResults = new MetricResults(); - namespaceMetricAggregationInfoResults = new MetricAggregationInfoResults(); - } - Method method = MetricAggregationInfoResults.class.getDeclaredMethod("set" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1), Double.class); - method.invoke(namespaceMetricAggregationInfoResults, value); - namespaceMetricAggregationInfoResults.setFormat(format); - namespaceMetricResults.setAggregationInfoResult(namespaceMetricAggregationInfoResults); - namespaceMetricResults.setName(String.valueOf(metricEntry.getKey())); - namespaceMetricResults.setFormat(format); - namespaceResMap.put(metricEntry.getKey(), namespaceMetricResults); - namespaceIntervalResults.setMetricResultsMap(namespaceResMap); - namespaceIntervalResults.setIntervalStartTime(sTime); - namespaceIntervalResults.setIntervalEndTime(eTime); - namespaceIntervalResults.setDurationInMinutes((double) ((eTime.getTime() - sTime.getTime()) - / ((long) KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE - * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC))); - namespaceDataResults.put(eTime, namespaceIntervalResults); - sTime = eTime; - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - } - } - namespaceData.setResults(namespaceDataResults); - if (namespaceDataResults.size() > 0) { - setInterval_end_time(Collections.max(namespaceDataResults.keySet())); - } -// LOGGER.info("NAMESPACE DATA OBJECT: "); -// LOGGER.info(k8sObject.getNamespaceData().toString()); - HashMap containerDataMap = k8sObject.getContainerDataMap(); // Iterate over containers if (!containerDataMap.isEmpty()) { @@ -2081,6 +1939,148 @@ public void fetchMetricsBasedOnDatasource(KruizeObject kruizeObject, Timestamp i setInterval_end_time(Collections.max(containerDataResults.keySet())); //TODO Temp fix invalide date is set if experiment having two container with different last seen date } } + + NamespaceData namespaceData = k8sObject.getNamespaceData(); + + // fetch namespace related metrics + if (null == interval_end_time) { + LOGGER.info("Determine the date of the last activity for the namespace based on container usage."); + String dateMetricsUrl = String.format(KruizeConstants.DataSourceConstants.DATE_ENDPOINT_WITH_QUERY, + dataSourceInfo.getUrl(), + URLEncoder.encode(String.format(PromQLDataSourceQueries.NAMESPACE_MAX_DATE, namespace), CHARACTER_ENCODING) + ); + LOGGER.info(dateMetricsUrl); + JSONObject genericJsonObject = new GenericRestApiClient(dateMetricsUrl).fetchMetricsJson("get", ""); + JsonObject jsonObject = new Gson().fromJson(genericJsonObject.toString(), JsonObject.class); + JsonArray resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray(KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT); + // Process fetched metrics + if (null != resultArray && !resultArray.isEmpty()) { + resultArray = resultArray.get(0).getAsJsonObject().getAsJsonArray("value"); + long epochTime = resultArray.get(0).getAsLong(); + String timestamp = sdf.format(new Date(epochTime * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC)); + Date date = sdf.parse(timestamp); + Timestamp dateTS = new Timestamp(date.getTime()); + interval_end_time_epoc = dateTS.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - ((long) dateTS.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); + int maxDay = Terms.getMaxDays(kruizeObject.getTerms()); + LOGGER.info("maxDay : {}", maxDay); + Timestamp startDateTS = Timestamp.valueOf(Objects.requireNonNull(dateTS).toLocalDateTime().minusDays(maxDay)); + interval_start_time_epoc = startDateTS.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC - ((long) startDateTS.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC); + } + } else { + // Convert timestamps to epoch time + interval_end_time_epoc = interval_end_time.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC + - ((long) interval_end_time.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); + interval_start_time_epoc = interval_start_time.getTime() / KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC + - ((long) interval_start_time.getTimezoneOffset() * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC); + } + + HashMap namespaceDataResults = new HashMap<>(); + IntervalResults namespaceIntervalResults; + HashMap namespaceResMap; + MetricResults namespaceMetricResults; + MetricAggregationInfoResults namespaceMetricAggregationInfoResults; + if (null == namespaceData) { + namespaceData = new NamespaceData(); + namespaceData.setNamespace_name(namespace); + k8sObject.setNamespaceData(namespaceData); + } + // Iterate over metrics and aggregation methods + for (Map.Entry metricEntry : promQls.entrySet()) { + if (metricEntry.getKey().name().startsWith("namespace")) { + for (String methodName : aggregationMethods) { + String promQL = null; + String format = null; + // Determine promQL and format based on metric type + if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuUsage || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuThrottle) && !methodName.equals(KruizeConstants.JSONKeys.SUM)) { + promQL = String.format(metricEntry.getValue(), methodName, namespace, measurementDurationMinutesInDouble.intValue()); + format = KruizeConstants.JSONKeys.CORES; + } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuLimit || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceCpuRequest) && methodName.equals(KruizeConstants.JSONKeys.SUM)) { + promQL = String.format(metricEntry.getValue(), methodName, namespace); + format = KruizeConstants.JSONKeys.CORES; + } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryUsage || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryRSS) && !methodName.equals(KruizeConstants.JSONKeys.SUM)) { + promQL = String.format(metricEntry.getValue(), methodName, namespace, measurementDurationMinutesInDouble.intValue()); + format = KruizeConstants.JSONKeys.BYTES; + } else if ((metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryLimit || metricEntry.getKey() == AnalyzerConstants.MetricName.namespaceMemoryRequest) && methodName.equals(KruizeConstants.JSONKeys.SUM)) { + promQL = String.format(metricEntry.getValue(), methodName, namespace); + format = KruizeConstants.JSONKeys.BYTES; + } + LOGGER.info("Metric Name: " + metricEntry.getKey() + " Method Name: " + methodName); + // If promQL is determined, fetch metrics from the datasource + if (promQL != null) { + LOGGER.info(promQL); + String namespaceMetricsUrl; + try { + namespaceMetricsUrl = String.format(KruizeConstants.DataSourceConstants.DATASOURCE_ENDPOINT_WITH_QUERY, + dataSourceInfo.getUrl(), + URLEncoder.encode(promQL, CHARACTER_ENCODING), + interval_start_time_epoc, + interval_end_time_epoc, + measurementDurationMinutesInDouble.intValue() * KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE); + LOGGER.info(namespaceMetricsUrl); + JSONObject genericJsonObject = new GenericRestApiClient(namespaceMetricsUrl).fetchMetricsJson("get", ""); + JsonObject jsonObject = new Gson().fromJson(genericJsonObject.toString(), JsonObject.class); + JsonArray resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray(KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT); + // Process fetched metrics + if (null != resultArray && !resultArray.isEmpty()) { + resultArray = jsonObject.getAsJsonObject(KruizeConstants.JSONKeys.DATA).getAsJsonArray( + KruizeConstants.DataSourceConstants.DataSourceQueryJSONKeys.RESULT).get(0) + .getAsJsonObject().getAsJsonArray(KruizeConstants.DataSourceConstants + .DataSourceQueryJSONKeys.VALUES); + sdf.setTimeZone(TimeZone.getTimeZone(KruizeConstants.TimeUnitsExt.TimeZones.UTC)); + + // Iterate over fetched metrics + Timestamp sTime = new Timestamp(interval_start_time_epoc); + for (JsonElement element : resultArray) { + JsonArray valueArray = element.getAsJsonArray(); + long epochTime = valueArray.get(0).getAsLong(); + double value = valueArray.get(1).getAsDouble(); + String timestamp = sdf.format(new Date(epochTime * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC)); + Date date = sdf.parse(timestamp); + Timestamp eTime = new Timestamp(date.getTime()); + + // Prepare interval results + if (namespaceDataResults.containsKey(eTime)) { + namespaceIntervalResults = namespaceDataResults.get(eTime); + namespaceResMap = namespaceIntervalResults.getMetricResultsMap(); + } else { + namespaceIntervalResults = new IntervalResults(); + namespaceResMap = new HashMap<>(); + } + if (namespaceResMap.containsKey(metricEntry.getKey())) { + namespaceMetricResults = namespaceResMap.get(metricEntry.getKey()); + namespaceMetricAggregationInfoResults = namespaceMetricResults.getAggregationInfoResult(); + } else { + namespaceMetricResults = new MetricResults(); + namespaceMetricAggregationInfoResults = new MetricAggregationInfoResults(); + } + Method method = MetricAggregationInfoResults.class.getDeclaredMethod("set" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1), Double.class); + method.invoke(namespaceMetricAggregationInfoResults, value); + namespaceMetricAggregationInfoResults.setFormat(format); + namespaceMetricResults.setAggregationInfoResult(namespaceMetricAggregationInfoResults); + namespaceMetricResults.setName(String.valueOf(metricEntry.getKey())); + namespaceMetricResults.setFormat(format); + namespaceResMap.put(metricEntry.getKey(), namespaceMetricResults); + namespaceIntervalResults.setMetricResultsMap(namespaceResMap); + namespaceIntervalResults.setIntervalStartTime(sTime); + namespaceIntervalResults.setIntervalEndTime(eTime); + namespaceIntervalResults.setDurationInMinutes((double) ((eTime.getTime() - sTime.getTime()) + / ((long) KruizeConstants.TimeConv.NO_OF_SECONDS_PER_MINUTE + * KruizeConstants.TimeConv.NO_OF_MSECS_IN_SEC))); + namespaceDataResults.put(eTime, namespaceIntervalResults); + sTime = eTime; + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + } + namespaceData.setResults(namespaceDataResults); + if (namespaceDataResults.size() > 0) { + setInterval_end_time(Collections.max(namespaceDataResults.keySet())); + } } } catch (Exception e) { e.printStackTrace();