public static async Task <HttpResponseMessage> RunAsync( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "analyze")] HttpRequestMessage request, TraceWriter log, ExecutionContext context, CancellationToken cancellationToken) { using (IUnityContainer childContainer = Container.CreateChildContainer().WithTracer(log, true)) { // Create a tracer for this run (that will also log to the specified TraceWriter) ITracer tracer = childContainer.Resolve <ITracer>(); tracer.TraceInformation($"Analyze function request received with invocation Id {context.InvocationId}"); tracer.AddCustomProperty("FunctionName", context.FunctionName); tracer.AddCustomProperty("InvocationId", context.InvocationId.ToString("N", CultureInfo.InvariantCulture)); try { // Trace app counters (before analysis) tracer.TraceAppCounters(); // Read the request SmartDetectorAnalysisRequest smartDetectorExecutionRequest = await request.Content.ReadAsAsync <SmartDetectorAnalysisRequest>(cancellationToken); tracer.AddCustomProperty("SmartDetectorId", smartDetectorExecutionRequest.SmartDetectorId); tracer.TraceInformation($"Analyze request received: {JsonConvert.SerializeObject(smartDetectorExecutionRequest)}"); // Process the request ISmartDetectorRunner runner = childContainer.Resolve <ISmartDetectorRunner>(); bool shouldDetectorTrace = bool.Parse(ConfigurationReader.ReadConfig("ShouldDetectorTrace", required: true)); List <ContractsAlert> alertPresentations = await runner.AnalyzeAsync(smartDetectorExecutionRequest, shouldDetectorTrace, cancellationToken); tracer.TraceInformation($"Analyze completed, returning {alertPresentations.Count} Alerts"); // Create the response with StringContent to prevent Json from serializing to a string var response = request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(JsonConvert.SerializeObject(alertPresentations), Encoding.UTF8, "application/json"); return(response); } catch (AnalysisFailedException afe) { // Handle the exception TopLevelExceptionHandler.TraceUnhandledException(afe, tracer, log); // Return error status return(request.CreateResponse(afe.StatusCode, afe.ReasonPhrase)); } catch (Exception e) { // Handle the exception TopLevelExceptionHandler.TraceUnhandledException(e, tracer, log); // Return error status return(request.CreateResponse(HttpStatusCode.InternalServerError, e.Message)); } finally { // Trace app counters (after analysis) tracer.TraceAppCounters(); } } }
/// <summary> /// Creates a presentation from an alert /// </summary> /// <param name="alert">The alert</param> /// <param name="request">The Smart Detector request</param> /// <param name="smartDetectorName">The Smart Detector name</param> /// <param name="usedLogAnalysisClient">Indicates whether a log analysis client was used to create the alert</param> /// <param name="usedMetricClient">Indicates whether a metric client was used to create the alert</param> /// <returns>The presentation</returns> public static ContractsAlert CreateContractsAlert(this Alert alert, SmartDetectorAnalysisRequest request, string smartDetectorName, bool usedLogAnalysisClient, bool usedMetricClient) { // A null alert has null presentation if (alert == null) { return(null); } // Create presentation elements for each alert property List <AlertProperty> alertProperties = alert.ExtractProperties(AlertBaseClassPropertiesNames); // Generate the alert's correlation hash based on its predicates string correlationHash = string.Join("##", alert.ExtractPredicates().OrderBy(x => x.Key).Select(x => x.Key + "|" + x.Value.ToString())).ToSha256Hash(); // Get the alert's signal type based on the clients used to create the alert SignalType signalType = GetSignalType(usedLogAnalysisClient, usedMetricClient); // Return the presentation object return(new ContractsAlert { Title = alert.Title, OccurenceTime = alert.OccurenceTime, ResourceId = alert.ResourceIdentifier.ToResourceId(), CorrelationHash = correlationHash, SmartDetectorId = request.SmartDetectorId, SmartDetectorName = smartDetectorName, AnalysisTimestamp = DateTime.UtcNow, AnalysisWindowSizeInMinutes = (int)request.Cadence.TotalMinutes, AlertProperties = alertProperties, SignalType = signalType, ResolutionParameters = alert.AlertResolutionParameters?.CreateContractsResolutionParameters() }); }
public void TestInitialize() { this.analysisRequest = new SmartDetectorAnalysisRequest { ResourceIds = new List<string>() { "resourceId" }, SmartDetectorId = "smartDetectorId", Cadence = TimeSpan.FromDays(1), }; }
/// <summary> /// Loads the Smart Detector, runs its analysis flow, and returns the generated alert. /// </summary> /// <param name="request">The Smart Detector analysis request</param> /// <param name="shouldDetectorTrace">Determines if the detector's traces are emitted</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A <see cref="Task{TResult}"/>, returning the list of Alerts generated by the Smart Detector.</returns> public async Task <List <ContractsAlert> > AnalyzeAsync(SmartDetectorAnalysisRequest request, bool shouldDetectorTrace, CancellationToken cancellationToken) { return(await this.LoadAndRunSmartDetector( request.SmartDetectorId, shouldDetectorTrace, request, this.AnalyzeAsync, cancellationToken)); }
/// <summary> /// Creates a new instance of the <see cref="AnalysisRequestParameters"/> class, based on <paramref name="request"/>. /// </summary> /// <param name="requestTime">The original time the analysis request was received from Azure Monitor back-end.</param> /// <param name="request">The analysis request received from Azure Monitoring back-end.</param> /// <param name="smartDetectorManifest">The Smart Detector's manifest, used for validations of the request.</param> /// <param name="shouldValidateResources">A value indicating whether we should validate that the request's resources are supported by the detector.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A <see cref="Task{TResult}"/>, returning the analysis request parameters.</returns> private async Task <AnalysisRequestParameters> CreateAnalysisRequestParametersAsync( DateTime requestTime, SmartDetectorAnalysisRequest request, SmartDetectorManifest smartDetectorManifest, bool shouldValidateResources, CancellationToken cancellationToken) { // Get the resources on which to run the Smart Detector List <ResourceIdentifier> resources = shouldValidateResources ? await this.GetResourcesForSmartDetector(request.ResourceIds, smartDetectorManifest, cancellationToken) : request.ResourceIds.Select(ResourceIdentifier.CreateFromResourceId).ToList(); return(new AnalysisRequestParameters(requestTime, resources, request.Cadence, request.AlertRuleResourceId, request.DetectorParameters)); }
/// <summary> /// Creating a new instance of <see cref="EmulationAlert"/> for unit tests. /// </summary> /// <param name="alert">The alert to wrap</param> /// <returns>An emulation alert</returns> public static EmulationAlert CreateEmulationAlert(Alert alert) { string resourceId = alert.ResourceIdentifier.ResourceType == ResourceType.ApplicationInsights ? appInsightsResourceId : virtualMachineResourceId; var request = new SmartDetectorAnalysisRequest { ResourceIds = new List <string> { resourceId }, SmartDetectorId = "smartDetectorId", Cadence = TimeSpan.FromDays(1), }; ContractsAlert contractsAlert = alert.CreateContractsAlert(request, "smartDetectorName", usedLogAnalysisClient: false, usedMetricClient: false); return(new EmulationAlert(contractsAlert, ExtendedDateTime.UtcNow)); }
/// <summary> /// Runs the Smart Detector analysis, in a separate process /// </summary> /// <param name="request">The request</param> /// <param name="shouldDetectorTrace">Determines if the detector's traces are emitted</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A <see cref="Task{TResult}"/>, returning the generated alerts presentations</returns> public async Task <List <Alert> > AnalyzeAsync(SmartDetectorAnalysisRequest request, bool shouldDetectorTrace, CancellationToken cancellationToken) { try { // Run the child process return(await this.childProcessManager.RunChildProcessAsync <List <Alert> >( this.GetSmartDetectorExecutablePath(), new SmartDetectorRunnerChildProcessInput { AnalysisRequest = request }, cancellationToken)); } catch (ChildProcessFailedException e) { if (Enum.IsDefined(typeof(HttpStatusCode), e.ExitCode)) { throw new AnalysisFailedException((HttpStatusCode)e.ExitCode, e.Message, e); } throw new AnalysisFailedException($"Running Smart Detector analysis in child process failed with exit code {e.ExitCode} and message: {e.Message}", e); } }
/// <summary> /// Runs the Smart Detector's analysis flow. /// </summary> /// <param name="request">The Smart Detector analysis request</param> /// <param name="smartDetector">The Smart Detector to run</param> /// <param name="smartDetectorManifest">The Smart Detector's manifest</param> /// <param name="detectorTracer">The tracer to provider for the Smart Detector</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A <see cref="Task{TResult}"/>, returning the list of Alerts generated by the Smart Detector.</returns> private async Task <List <ContractsAlert> > AnalyzeAsync( SmartDetectorAnalysisRequest request, ISmartDetector smartDetector, SmartDetectorManifest smartDetectorManifest, ITracer detectorTracer, CancellationToken cancellationToken) { // Create state repository IStateRepository stateRepository = this.stateRepositoryFactory.Create(request.SmartDetectorId, request.AlertRuleResourceId); // Create the input for the Smart Detector AnalysisRequestParameters analysisRequestParameters = await this.CreateAnalysisRequestParametersAsync(DateTime.UtcNow, request, smartDetectorManifest, true, cancellationToken); var analysisRequest = new AnalysisRequest(analysisRequestParameters, this.analysisServicesFactory, stateRepository); // Run the Smart Detector this.tracer.TraceInformation($"Started running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}"); List <Alert> alerts; try { alerts = await smartDetector.AnalyzeResourcesAsync(analysisRequest, detectorTracer, cancellationToken); this.tracer.TraceInformation( $"Completed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}, returning {alerts.Count} alerts"); } catch (DetectorNotReadyException dnre) { this.tracer.TraceWarning($"Smart Detector is not ready to run analysis yet, aborting analysis: {dnre.Message}"); return(new List <ContractsAlert>()); } catch (Exception e) { this.tracer.TraceError($"Failed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}: {e}"); throw new FailedToRunSmartDetectorException($"Calling Smart Detector '{smartDetectorManifest.Name}' failed with exception of type {e.GetType()} and message: {e.Message}", e); } // Verify that each alert belongs to one of the types declared in the Smart Detector manifest foreach (Alert alert in alerts) { if (!smartDetectorManifest.SupportedResourceTypes.Contains(alert.ResourceIdentifier.ResourceType)) { throw new UnidentifiedAlertResourceTypeException(alert.ResourceIdentifier); } } // Trace the number of alerts of each type foreach (var alertType in alerts.GroupBy(x => x.GetType().Name)) { this.tracer.TraceInformation($"Got {alertType.Count()} Alerts of type '{alertType.Key}'"); this.tracer.ReportMetric("AlertType", alertType.Count(), new Dictionary <string, string>() { { "AlertType", alertType.Key } }); } // Create results bool detectorSupportsAlertResolution = smartDetector is IResolvableAlertSmartDetector; List <ContractsAlert> results = new List <ContractsAlert>(); foreach (var alert in alerts) { ContractsAlert contractsAlert = alert.CreateContractsAlert( request, smartDetectorManifest.Name, this.analysisServicesFactory.UsedLogAnalysisClient, this.analysisServicesFactory.UsedMetricClient); // Handle resolution parameters in the alerts: // If the detector supports resolution - save the predicates for the resolution checks // If the detector doesn't support resolution - drop the resolution parameters (since they are useless) and error trace if (contractsAlert.ResolutionParameters != null) { if (detectorSupportsAlertResolution) { this.tracer.TraceInformation($"Alert {contractsAlert.CorrelationHash} has resolution parameters, so saving alert details for later use"); await stateRepository.StoreStateAsync( GetResolutionStateKey(contractsAlert.CorrelationHash), new ResolutionState { AnalysisRequestTime = analysisRequestParameters.RequestTime, AlertPredicates = alert.ExtractPredicates() }, cancellationToken); } else { this.tracer.TraceError($"Dropping resolution parameters from alert {contractsAlert.CorrelationHash}"); contractsAlert.ResolutionParameters = null; } } // And add the alert to the results results.Add(contractsAlert); } this.tracer.TraceInformation($"Returning {results.Count} results"); return(results); }
private void TestInitialize(ResourceType requestResourceType, ResourceType smartDetectorResourceType) { this.testContainer = new UnityContainer(); this.testContainer.RegisterType <ISmartDetectorRunner, SmartDetectorRunner>(); this.testContainer.RegisterInstance(new Mock <ITracer>().Object); ResourceIdentifier resourceId; switch (requestResourceType) { case ResourceType.Subscription: resourceId = new ResourceIdentifier(requestResourceType, "subscriptionId", string.Empty, string.Empty); break; case ResourceType.ResourceGroup: resourceId = new ResourceIdentifier(requestResourceType, "subscriptionId", "resourceGroup", string.Empty); break; default: resourceId = new ResourceIdentifier(requestResourceType, "subscriptionId", "resourceGroup", "resourceName"); break; } this.resourceIds = new List <string> { resourceId.ToResourceId() }; this.analysisRequest = new SmartDetectorAnalysisRequest { ResourceIds = this.resourceIds, Cadence = TimeSpan.FromDays(1), AlertRuleResourceId = "alertRule", SmartDetectorId = "1", DetectorParameters = new Dictionary <string, object> { { "param1", "value1" }, { "param2", 2 }, } }; this.alertResolutionCheckRequest = new ContractsAlertResolutionCheckRequest { OriginalAnalysisRequest = new SmartDetectorAnalysisRequest { ResourceIds = this.resourceIds, Cadence = TimeSpan.FromDays(1), AlertRuleResourceId = "alertRule", SmartDetectorId = "2", DetectorParameters = new Dictionary <string, object> { { "param1", "value1" }, { "param2", 2 }, } }, AlertCorrelationHash = "correlationHash", TargetResource = resourceId.ToResourceId(), AlertFireTime = new DateTime(1985, 7, 3) }; var smartDetectorManifest = new SmartDetectorManifest( "1", "Test Smart Detector", "Test Smart Detector description", Version.Parse("1.0"), "TestSmartDetectorLibrary", "class", new List <ResourceType>() { smartDetectorResourceType }, new List <int> { 60 }, null, null); this.smartDetectorPackage = new SmartDetectorPackage(new Dictionary <string, byte[]> { ["manifest.json"] = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(smartDetectorManifest)), ["TestSmartDetectorLibrary"] = Array.Empty <byte>(), }); var autoResolveSmartDetectorManifest = new SmartDetectorManifest( "2", "Test Auto Resolve Smart Detector", "Test Auto Resolve Smart Detector description", Version.Parse("1.0"), "TestSmartDetectorLibrary", "class", new List <ResourceType>() { smartDetectorResourceType }, new List <int> { 60 }, null, null); this.autoResolveSmartDetectorPackage = new SmartDetectorPackage(new Dictionary <string, byte[]> { ["manifest.json"] = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(autoResolveSmartDetectorManifest)), ["TestSmartDetectorLibrary"] = Array.Empty <byte>(), }); var smartDetectorRepositoryMock = new Mock <ISmartDetectorRepository>(); smartDetectorRepositoryMock .Setup(x => x.ReadSmartDetectorPackageAsync("1", It.IsAny <CancellationToken>())) .ReturnsAsync(() => this.smartDetectorPackage); smartDetectorRepositoryMock .Setup(x => x.ReadSmartDetectorPackageAsync("2", It.IsAny <CancellationToken>())) .ReturnsAsync(() => this.autoResolveSmartDetectorPackage); this.testContainer.RegisterInstance(smartDetectorRepositoryMock.Object); this.testContainer.RegisterInstance(new Mock <IInternalAnalysisServicesFactory>().Object); this.smartDetector = new TestSmartDetector { ExpectedResourceType = smartDetectorResourceType }; this.autoResolveSmartDetector = new TestAutoResolveSmartDetector { ExpectedResourceType = smartDetectorResourceType }; var smartDetectorLoaderMock = new Mock <ISmartDetectorLoader>(); smartDetectorLoaderMock .Setup(x => x.LoadSmartDetector(this.smartDetectorPackage)) .Returns(() => this.smartDetector); smartDetectorLoaderMock .Setup(x => x.LoadSmartDetector(this.autoResolveSmartDetectorPackage)) .Returns(() => this.autoResolveSmartDetector); this.testContainer.RegisterInstance(smartDetectorLoaderMock.Object); var azureResourceManagerClientMock = new Mock <IExtendedAzureResourceManagerClient>(); azureResourceManagerClientMock .Setup(x => x.GetAllResourceGroupsInSubscriptionAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .ReturnsAsync((string subscriptionId, CancellationToken cancellationToken) => new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.ResourceGroup, subscriptionId, "resourceGroupName", string.Empty) }); azureResourceManagerClientMock .Setup(x => x.GetAllResourcesInSubscriptionAsync(It.IsAny <string>(), It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>())) .ReturnsAsync((string subscriptionId, IEnumerable <ResourceType> resourceTypes, CancellationToken cancellationToken) => new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.VirtualMachine, subscriptionId, "resourceGroupName", "resourceName") }); azureResourceManagerClientMock .Setup(x => x.GetAllResourcesInResourceGroupAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>())) .ReturnsAsync((string subscriptionId, string resourceGroupName, IEnumerable <ResourceType> resourceTypes, CancellationToken cancellationToken) => new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.VirtualMachine, subscriptionId, resourceGroupName, "resourceName") }); this.testContainer.RegisterInstance(azureResourceManagerClientMock.Object); this.stateRepository = new Dictionary <string, object>(); this.stateRepositoryMock = new Mock <IStateRepository>(); this.stateRepositoryMock .Setup(m => m.StoreStateAsync(It.IsAny <string>(), It.IsAny <object>(), It.IsAny <CancellationToken>())) .Callback <string, object, CancellationToken>((key, value, token) => this.stateRepository[key] = value) .Returns(Task.CompletedTask); this.stateRepositoryMock .Setup(m => m.GetStateAsync <ResolutionState>(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns <string, CancellationToken>((key, token) => Task.FromResult((ResolutionState)(this.stateRepository.ContainsKey(key) ? this.stateRepository[key] : null))); this.stateRepositoryMock .Setup(m => m.DeleteStateAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Callback <string, CancellationToken>((key, token) => this.stateRepository.Remove(key)) .Returns(Task.CompletedTask); this.stateRepositoryFactoryMock = new Mock <IStateRepositoryFactory>(); this.stateRepositoryFactoryMock.Setup(m => m.Create(It.IsAny <string>(), It.IsAny <string>())).Returns(this.stateRepositoryMock.Object); this.testContainer.RegisterInstance(this.stateRepositoryFactoryMock.Object); }