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();
                }
            }
        }
Esempio n. 2
0
        /// <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));
        }
Esempio n. 7
0
        /// <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);
        }