public void WhenTryingToFindExistingResourceThenThisResourceWasFound()
        {
            // Create hierarchical resources structure: { subscription } -> { resourceGroup } -> { appInsights, virtualMachine }
            ResourceIdentifier          resourceGroupIdentifier = ResourceIdentifier.CreateFromResourceId("/subscriptions/7904b7bd-5e6b-4415-99a8-355657b7da19/resourceGroups/MyResourceGroupName");
            List <HierarchicalResource> groupContainedResources = new List <HierarchicalResource>()
            {
                this.appInsightsHierarchicalResource, this.virtualMachineHierarchicalResource
            };
            var resourceGroupHierarchicalResource = new HierarchicalResource(resourceGroupIdentifier, groupContainedResources, resourceGroupIdentifier.ResourceGroupName);

            ResourceIdentifier          subscriptionIdentifier         = ResourceIdentifier.CreateFromResourceId("/subscriptions/7904b7bd-5e6b-4415-99a8-355657b7da19");
            List <HierarchicalResource> subscriptionContainedResources = new List <HierarchicalResource>()
            {
                resourceGroupHierarchicalResource
            };
            var subscriptionHierarchicalResource = new HierarchicalResource(subscriptionIdentifier, subscriptionContainedResources, subscriptionIdentifier.SubscriptionId);

            // Verify hierarchical structure before filtering
            CollectionAssert.AreEqual(subscriptionContainedResources, subscriptionHierarchicalResource.ContainedResources.OriginalCollection);
            CollectionAssert.AreEqual(subscriptionContainedResources, subscriptionHierarchicalResource.ContainedResources.FilteredCollection);

            CollectionAssert.AreEqual(groupContainedResources, resourceGroupHierarchicalResource.ContainedResources.OriginalCollection);
            CollectionAssert.AreEqual(groupContainedResources, resourceGroupHierarchicalResource.ContainedResources.FilteredCollection);

            Assert.IsNotNull(subscriptionHierarchicalResource.TryFind("someApp"));
            Assert.IsNotNull(subscriptionHierarchicalResource.TryFind("MyVirtualMachineName"));

            // Filter all resources but virtualMachine
            subscriptionHierarchicalResource.ContainedResources.Filter = this.FilterAllResourcesButVirtualMachine;

            Assert.IsNotNull(subscriptionHierarchicalResource.TryFind("someApp"));
            Assert.IsNull(subscriptionHierarchicalResource.TryFind("MyVirtualMachineName"));
        }
        public void Setup()
        {
            ResourceIdentifier appInsightsResourceIdentifier = ResourceIdentifier.CreateFromResourceId("/subscriptions/7904b7bd-5e6b-4415-99a8-355657b7da19/resourceGroups/MyResourceGroupName/providers/microsoft.insights/components/someApp");

            this.appInsightsHierarchicalResource = new HierarchicalResource(appInsightsResourceIdentifier, new List <HierarchicalResource>(), appInsightsResourceIdentifier.ResourceName);

            ResourceIdentifier virtualMachineResourceIdentifier = ResourceIdentifier.CreateFromResourceId("/subscriptions/7904b7bd-5e6b-4415-99a8-355657b7da19/resourceGroups/MyResourceGroupName/providers/Microsoft.Compute/virtualMachines/MyVirtualMachineName");

            this.virtualMachineHierarchicalResource = new HierarchicalResource(virtualMachineResourceIdentifier, new List <HierarchicalResource>(), virtualMachineResourceIdentifier.ResourceName);
        }
Esempio n. 3
0
        /// <summary>
        /// Enumerates all the resource groups in the specified subscription.
        /// </summary>
        /// <param name="subscriptionId">The subscription ID.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning the resource groups.</returns>
        public async Task <IList <ResourceIdentifier> > GetAllResourceGroupsInSubscriptionAsync(string subscriptionId, CancellationToken cancellationToken)
        {
            ResourceManagementClient resourceManagementClient = this.GetResourceManagementClient(subscriptionId);

            Task <IPage <ResourceGroupInner> > FirstPage() => resourceManagementClient.ResourceGroups.ListAsync(cancellationToken: cancellationToken);
            Task <IPage <ResourceGroupInner> > NextPage(string nextPageLink) => resourceManagementClient.ResourceGroups.ListNextAsync(nextPageLink, cancellationToken);

            return((await this.RunAndTrack(() => this.ReadAllPages(FirstPage, NextPage)))
                   .Select(resourceGroup => ResourceIdentifier.CreateFromResourceId(resourceGroup.Id))
                   .ToList());
        }
Esempio n. 4
0
        /// <summary>
        /// Enumerates all the resources in the specified resource group.
        /// </summary>
        /// <param name="subscriptionId">The subscription ID.</param>
        /// <param name="resourceGroupName">The resource group name.</param>
        /// <param name="resourceTypes">The types of resource to enumerate.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning the resource identifiers.</returns>
        public async Task <IList <ResourceIdentifier> > GetAllResourcesInResourceGroupAsync(string subscriptionId, string resourceGroupName, IEnumerable <ResourceType> resourceTypes, CancellationToken cancellationToken)
        {
            ResourceManagementClient resourceManagementClient = this.GetResourceManagementClient(subscriptionId);
            ODataQuery <GenericResourceFilterInner> query     = GetResourcesByTypeQuery(resourceTypes);

            Task <IPage <GenericResourceInner> > FirstPage() => resourceManagementClient.Resources.ListByResourceGroupAsync(resourceGroupName, query, cancellationToken);
            Task <IPage <GenericResourceInner> > NextPage(string nextPageLink) => resourceManagementClient.Resources.ListByResourceGroupNextAsync(nextPageLink, cancellationToken);

            return((await this.RunAndTrack(() => this.ReadAllPages(FirstPage, NextPage)))
                   .Select(resource => ResourceIdentifier.CreateFromResourceId(resource.Id))
                   .ToList());
        }
        /// <summary>
        /// Runs the smart signal.
        /// </summary>
        /// <param name="resources">The resources which the signal should run on.</param>
        /// <param name="analysisCadence">The analysis cadence.</param>
        /// <returns>A task that runs the smart signal.</returns>
        public async Task RunAsync(List <ResourceIdentifier> resources, TimeSpan analysisCadence)
        {
            this.cancellationTokenSource = new CancellationTokenSource();
            this.Results.Clear();
            var analysisRequest = new AnalysisRequest(resources, null, analysisCadence, this.analysisServicesFactory);

            try
            {
                // Run Signal
                this.IsSignalRunning = true;

                SmartSignalResult signalResults = await this.smartSignal.AnalyzeResourcesAsync(
                    analysisRequest,
                    this.Tracer,
                    this.cancellationTokenSource.Token);

                // Create signal result items
                List <SignalResultItem> signalResultItems = new List <SignalResultItem>();
                foreach (var resultItem in signalResults.ResultItems)
                {
                    // Create result item presentation
                    var resourceIds          = resources.Select(resource => resource.ResourceName).ToList();
                    var smartSignalsSettings = new SmartSignalSettings();
                    var smartSignalRequest   = new SmartSignalRequest(resourceIds, this.smartSignalManifes.Id, null, analysisCadence, smartSignalsSettings);
                    SmartSignalResultItemQueryRunInfo queryRunInfo = await this.queryRunInfoProvider.GetQueryRunInfoAsync(new List <ResourceIdentifier>() { resultItem.ResourceIdentifier }, this.cancellationTokenSource.Token);

                    SmartSignalResultItemPresentation resultItemPresentation = SmartSignalResultItemPresentation.CreateFromResultItem(
                        smartSignalRequest, this.smartSignalManifes.Name, resultItem, queryRunInfo);

                    // Create Azure resource identifier
                    ResourceIdentifier resourceIdentifier = ResourceIdentifier.CreateFromResourceId(resultItemPresentation.ResourceId);

                    signalResultItems.Add(new SignalResultItem(resultItemPresentation, resourceIdentifier));
                }

                this.Results = new ObservableCollection <SignalResultItem>(signalResultItems);
                this.tracer.TraceInformation($"Found {this.Results.Count} results");
            }
            catch (OperationCanceledException)
            {
                this.Tracer.TraceError("Signal run was canceled.");
            }
            catch (Exception e)
            {
                this.Tracer.ReportException(e);
            }
            finally
            {
                this.IsSignalRunning = false;
                this.cancellationTokenSource?.Dispose();
            }
        }
        private void VerifyConversion(string testResourceId, ResourceIdentifier testResourceIdentifier)
        {
            var resourceIdentifier = ResourceIdentifier.CreateFromResourceId(testResourceId);
            var resourceId         = resourceIdentifier.ToResourceId();

            Assert.AreEqual(testResourceId, resourceId, "Resource IDs are different");
            Assert.AreEqual(testResourceIdentifier, resourceIdentifier, "Resource identifiers are are different");

            resourceId         = testResourceIdentifier.ToResourceId();
            resourceIdentifier = ResourceIdentifier.CreateFromResourceId(resourceId);
            Assert.AreEqual(testResourceId, resourceId, "Resource IDs are different");
            Assert.AreEqual(testResourceIdentifier, resourceIdentifier, "Resource identifiers are are different");
        }
        /// <summary>
        /// Creates an instance of <see cref="ITelemetryDataClient"/>, used for running queries against data in Application Insights.
        /// </summary>
        /// <param name="resources">The list of resources to analyze.</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <exception cref="TelemetryDataClientCreationException">An Application Insights telemetry data client could not be created for the specified resources.</exception>
        /// <returns>The telemetry data client, that can be used to run queries on Application Insights.</returns>
        public async Task <ITelemetryDataClient> CreateApplicationInsightsTelemetryDataClientAsync(IReadOnlyList <ResourceIdentifier> resources, CancellationToken cancellationToken)
        {
            // Get the query run info, and verify it
            QueryRunInfo runInfo = await this.queryRunInfoProvider.GetQueryRunInfoAsync(resources, cancellationToken);

            this.VerifyRunInfo(runInfo, TelemetryDbType.ApplicationInsights);

            // Get application Id (for the 1st application)
            ResourceIdentifier firstApplication   = ResourceIdentifier.CreateFromResourceId(runInfo.ResourceIds[0]);
            string             firstApplicationId = await this.azureResourceManagerClient.GetApplicationInsightsAppIdAsync(firstApplication, cancellationToken);

            // Create the client
            return(new ApplicationInsightsTelemetryDataClient(this.tracer, this.httpClientWrapper, this.credentialsFactory, firstApplicationId, resources.Select(resource => resource.ToResourceId()), this.queryTimeout));
        }
        /// <summary>
        /// Creates an instance of <see cref="ITelemetryDataClient"/>, used for running queries against data in log analytics workspaces.
        /// </summary>
        /// <param name="resources">The list of resources to analyze.</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <exception cref="TelemetryDataClientCreationException">A log analytics telemetry data client could not be created for the specified resources.</exception>
        /// <returns>The telemetry data client, that can be used to run queries on log analytics workspaces.</returns>
        public async Task <ITelemetryDataClient> CreateLogAnalyticsTelemetryDataClientAsync(IReadOnlyList <ResourceIdentifier> resources, CancellationToken cancellationToken)
        {
            // Get the query run info, and verify it
            QueryRunInfo runInfo = await this.queryRunInfoProvider.GetQueryRunInfoAsync(resources, cancellationToken);

            this.VerifyRunInfo(runInfo, TelemetryDbType.LogAnalytics);

            // Get workspace Id (for the 1st workspace)
            ResourceIdentifier firstWorkspace   = ResourceIdentifier.CreateFromResourceId(runInfo.ResourceIds[0]);
            string             firstWorkspaceId = await this.azureResourceManagerClient.GetLogAnalyticsWorkspaceIdAsync(firstWorkspace, cancellationToken);

            // Create the client
            return(new LogAnalyticsTelemetryDataClient(this.tracer, this.httpClientWrapper, this.credentialsFactory, firstWorkspaceId, runInfo.ResourceIds, this.queryTimeout));
        }
        public void WhenCreatingNewHierarchicalResourceThenItWasInitializedCorrectly()
        {
            ResourceIdentifier          resourceGroupIdentifier = ResourceIdentifier.CreateFromResourceId("/subscriptions/7904b7bd-5e6b-4415-99a8-355657b7da19/resourceGroups/MyResourceGroupName");
            List <HierarchicalResource> groupContainedResources = new List <HierarchicalResource>()
            {
                this.appInsightsHierarchicalResource, this.virtualMachineHierarchicalResource
            };
            var resourceGroupHierarchicalResource = new HierarchicalResource(resourceGroupIdentifier, groupContainedResources, resourceGroupIdentifier.ResourceGroupName);

            Assert.AreEqual(resourceGroupIdentifier, resourceGroupHierarchicalResource.ResourceIdentifier, "Unexpected resource identifier");
            Assert.AreEqual(resourceGroupIdentifier.ResourceGroupName, resourceGroupHierarchicalResource.Name, "Unexpected resource name");
            CollectionAssert.AreEqual(groupContainedResources, resourceGroupHierarchicalResource.ContainedResources.OriginalCollection, "Unexpected resource's original contained resources");
            CollectionAssert.AreEqual(groupContainedResources, resourceGroupHierarchicalResource.ContainedResources.FilteredCollection, "Unexpected resource's filtered contained resources");
        }
Esempio n. 10
0
        /// <summary>
        /// Verify that the request resource type is supported by the Smart Detector, and enumerate
        /// the resources that the Smart Detector should run on.
        /// </summary>
        /// <param name="requestResourceIds">The request resource Ids</param>
        /// <param name="smartDetectorManifest">The Smart Detector manifest</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning the resource identifiers that the Smart Detector should run on</returns>
        private async Task <List <ResourceIdentifier> > GetResourcesForSmartDetector(IList <string> requestResourceIds, SmartDetectorManifest smartDetectorManifest, CancellationToken cancellationToken)
        {
            HashSet <ResourceIdentifier> resourcesForSmartDetector = new HashSet <ResourceIdentifier>();

            foreach (string requestResourceId in requestResourceIds)
            {
                ResourceIdentifier requestResource = ResourceIdentifier.CreateFromResourceId(requestResourceId);

                if (smartDetectorManifest.SupportedResourceTypes.Contains(requestResource.ResourceType))
                {
                    // If the Smart Detector directly supports the requested resource type, then that's it
                    resourcesForSmartDetector.Add(requestResource);
                }
                else if (requestResource.ResourceType == ResourceType.Subscription && smartDetectorManifest.SupportedResourceTypes.Contains(ResourceType.ResourceGroup))
                {
                    // If the request is for a subscription, and the Smart Detector supports a resource group type, enumerate all resource groups in the requested subscription
                    IList <ResourceIdentifier> resourceGroups = await this.azureResourceManagerClient.GetAllResourceGroupsInSubscriptionAsync(requestResource.SubscriptionId, cancellationToken);

                    resourcesForSmartDetector.UnionWith(resourceGroups);
                    this.tracer.TraceInformation($"Added {resourceGroups.Count} resource groups found in subscription {requestResource.SubscriptionId}");
                }
                else if (requestResource.ResourceType == ResourceType.Subscription)
                {
                    // If the request is for a subscription, enumerate all the resources in the requested subscription that the Smart Detector supports
                    IList <ResourceIdentifier> resources = await this.azureResourceManagerClient.GetAllResourcesInSubscriptionAsync(requestResource.SubscriptionId, smartDetectorManifest.SupportedResourceTypes, cancellationToken);

                    resourcesForSmartDetector.UnionWith(resources);
                    this.tracer.TraceInformation($"Added {resources.Count} resources found in subscription {requestResource.SubscriptionId}");
                }
                else if (requestResource.ResourceType == ResourceType.ResourceGroup && smartDetectorManifest.SupportedResourceTypes.Any(type => type != ResourceType.Subscription))
                {
                    // If the request is for a resource group, and the Smart Detector supports resource types (other than subscription),
                    // enumerate all the resources in the requested resource group that the Smart Detector supports
                    IList <ResourceIdentifier> resources = await this.azureResourceManagerClient.GetAllResourcesInResourceGroupAsync(requestResource.SubscriptionId, requestResource.ResourceGroupName, smartDetectorManifest.SupportedResourceTypes, cancellationToken);

                    resourcesForSmartDetector.UnionWith(resources);
                    this.tracer.TraceInformation($"Added {resources.Count} resources found in the specified resource group in subscription {requestResource.SubscriptionId}");
                }
                else
                {
                    // The Smart Detector does not support the requested resource type
                    throw new IncompatibleResourceTypesException(requestResource.ResourceType, smartDetectorManifest);
                }
            }

            return(resourcesForSmartDetector.ToList());
        }
Esempio n. 11
0
        /// <summary>
        /// Returns telemetry id of the first resource in the supplied telemetry resources list,
        /// and removes that first resource from the list.
        /// If the first resource doesn't exist, the method will remove it from the list and continue
        /// until an existing resource is found (in which case the method will return that resource's
        /// telemetry id, and will remove that resource from the list).
        /// </summary>
        /// <param name="telemetryResourceIds">Telemetry resources to filter</param>
        /// <param name="cancellationToken">The cancellation token to use.</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning telemetry id or null if none of supplied telemetry resources were found</returns>
        private async Task <string> GetSingleTelemetryIdAsync(List <string> telemetryResourceIds, CancellationToken cancellationToken)
        {
            string telemetryId = null;

            while (telemetryId == null && telemetryResourceIds.Count > 0)
            {
                string candidateTelemetryResourceId = null;
                try
                {
                    candidateTelemetryResourceId = telemetryResourceIds.First();
                    telemetryId = await this.GetTelemetryResourceIdAsync(ResourceIdentifier.CreateFromResourceId(candidateTelemetryResourceId), cancellationToken);
                }
                catch (AzureResourceManagerClientException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                {
                    this.tracer.TraceWarning($"Telemetry resource '{candidateTelemetryResourceId}' is not found{(telemetryResourceIds.Count > 1 ? ", will attempt to get telemetry id from another resource" : string.Empty)}");
                }

                telemetryResourceIds.Remove(candidateTelemetryResourceId);
            }

            return(telemetryId);
        }
Esempio n. 12
0
        /// <summary>
        /// Returns a filtered subset of supplied list of resources, containing only currently existing resources
        /// </summary>
        /// <param name="telemetryResourceIds">Telemetry resources to filter</param>
        /// <param name="cancellationToken">The cancellation token to use.</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning filtered list of existing resources.</returns>
        private async Task <List <string> > FilterDeletedTelemetryResourcesAsync(IReadOnlyList <string> telemetryResourceIds, CancellationToken cancellationToken)
        {
            var filteredTelemetryResourceIds = new List <string>();

            foreach (var telemetryResourceId in telemetryResourceIds)
            {
                try
                {
                    await this.AzureResourceManagerClient.GetResourcePropertiesAsync(ResourceIdentifier.CreateFromResourceId(telemetryResourceId), cancellationToken);
                }
                catch (AzureResourceManagerClientException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                {
                    this.tracer.TraceWarning($"Telemetry resource '{telemetryResourceId}' is not found, skipping this resource");
                    continue;
                }

                filteredTelemetryResourceIds.Add(telemetryResourceId);
            }

            this.tracer.TraceInformation($"{filteredTelemetryResourceIds.Count} out of {telemetryResourceIds.Count} telemetry resources left after filtration");

            return(filteredTelemetryResourceIds);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="EmulationAlert"/> class
 /// </summary>
 /// <param name="contractsAlert">The alert presentation object</param>
 /// <param name="emulationIterationDate">The timestamp of the emulation iteration</param>
 public EmulationAlert(ContractsAlert contractsAlert, DateTime emulationIterationDate)
 {
     this.ContractsAlert         = contractsAlert;
     this.ResourceIdentifier     = ResourceIdentifier.CreateFromResourceId(contractsAlert.ResourceId);
     this.EmulationIterationDate = emulationIterationDate;
 }
Esempio n. 14
0
        /// <summary>
        /// Runs the Smart Detector.
        /// </summary>
        /// <param name="resources">The resources which the Smart Detector should run on</param>
        /// <param name="analysisCadence">The analysis cadence</param>
        /// <param name="startTimeRange">The start time</param>
        /// <param name="endTimeRange">The end time</param>
        /// <returns>A task that runs the Smart Detector</returns>
        public async Task RunAsync(List <ResourceIdentifier> resources, TimeSpan analysisCadence, DateTime startTimeRange, DateTime endTimeRange)
        {
            this.cancellationTokenSource = new CancellationTokenSource();
            IStateRepository stateRepository = this.stateRepositoryFactory.Create(this.smartDetectorId);

            List <string> resourceIds = resources.Select(resource => resource.ResourceName).ToList();

            this.Alerts.Clear();
            try
            {
                // Run Smart Detector
                this.IsSmartDetectorRunning = true;

                int totalRunsAmount  = (int)((endTimeRange.Subtract(startTimeRange).Ticks / analysisCadence.Ticks) + 1);
                int currentRunNumber = 1;
                for (var currentTime = startTimeRange; currentTime <= endTimeRange; currentTime = currentTime.Add(analysisCadence))
                {
                    this.tracer.TraceInformation($"Start analysis, end of time range: {currentTime}");

                    var analysisRequest = new AnalysisRequest(resources, currentTime, analysisCadence, null, this.analysisServicesFactory, stateRepository);
                    var newAlerts       = await this.smartDetector.AnalyzeResourcesAsync(
                        analysisRequest,
                        this.Tracer,
                        this.cancellationTokenSource.Token);

                    var smartDetectorExecutionRequest = new SmartDetectorExecutionRequest
                    {
                        ResourceIds     = resourceIds,
                        SmartDetectorId = this.smartDetectorManifes.Id,
                        Cadence         = analysisCadence,
                        DataEndTime     = currentTime
                    };

                    // Show the alerts that were found in this iteration
                    newAlerts.ForEach(async newAlert =>
                    {
                        QueryRunInfo queryRunInfo = await this.queryRunInfoProvider.GetQueryRunInfoAsync(new List <ResourceIdentifier>()
                        {
                            newAlert.ResourceIdentifier
                        }, this.cancellationTokenSource.Token);
                        ContractsAlert contractsAlert = newAlert.CreateContractsAlert(smartDetectorExecutionRequest, this.smartDetectorManifes.Name, queryRunInfo);

                        // Create Azure resource identifier
                        ResourceIdentifier resourceIdentifier = ResourceIdentifier.CreateFromResourceId(contractsAlert.ResourceId);

                        this.Alerts.Add(new EmulationAlert(contractsAlert, resourceIdentifier, currentTime));
                    });

                    this.tracer.TraceInformation($"completed {currentRunNumber} of {totalRunsAmount} runs");
                    currentRunNumber++;
                }

                string separator = "=====================================================================================================";
                this.tracer.TraceInformation($"Total alerts found: {this.Alerts.Count} {Environment.NewLine} {separator}");
            }
            catch (OperationCanceledException)
            {
                this.Tracer.TraceError("Smart Detector run was canceled.");
            }
            catch (Exception e)
            {
                this.Tracer.ReportException(e);
            }
            finally
            {
                this.IsSmartDetectorRunning = false;
                this.cancellationTokenSource?.Dispose();
            }
        }
            public async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, TimeSpan?timeout, CancellationToken cancellationToken)
            {
                this.CallsToSendAsync++;

                JObject content = (JObject)JsonConvert.DeserializeObject(await request.Content.ReadAsStringAsync());

                string extractedTelemetryId = request.RequestUri.ToString().Split('/')[5];

                List <string> telemetryResources = new List <string>();

                if (content.ContainsKey("applications"))
                {
                    telemetryResources.AddRange((content["applications"] as JArray).Values <string>());
                }
                else if (content.ContainsKey("workspaces"))
                {
                    telemetryResources.AddRange((content["workspaces"] as JArray).Values <string>());
                }

                // Prepare error response
                JObject errorObject = new JObject()
                {
                    ["error"] = new JObject()
                    {
                        ["message"]    = "test error message",
                        ["code"]       = "test error code",
                        ["innererror"] = new JObject()
                        {
                            ["code"]    = "test inner error code",
                            ["message"] = "test inner error message"
                        }
                    }
                };

                var errorResponse = new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new StringContent(errorObject.ToString())
                };

                // Return error if one of resources is missing, or if the telemetry Id in the URL exists in the list
                if (telemetryResources.Any(resource => this.telemetryResourceIdToTelemetryIdMapping.ContainsKey(ResourceIdentifier.CreateFromResourceId(resource)) == false) ||
                    telemetryResources.Any(resource => resource == extractedTelemetryId))
                {
                    return(errorResponse);
                }

                telemetryResources = telemetryResources.Select(resource => this.telemetryResourceIdToTelemetryIdMapping[ResourceIdentifier.CreateFromResourceId(resource)]).ToList();
                if (telemetryResources.Contains(extractedTelemetryId))
                {
                    return(errorResponse);
                }

                telemetryResources.Add(extractedTelemetryId);

                return(new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(this.MergeJObjectTables(telemetryResources.Select(GetExpectedResultForWorkspaceAsJObject)).ToString())
                });
            }
Esempio n. 16
0
        /// <summary>
        /// Sends the Smart Signal result Email.
        /// </summary>
        /// <param name="signalExecution">The signals execution information.</param>
        /// <param name="smartSignalResultItems">The Smart Signal result items.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public async Task SendSignalResultEmailAsync(SignalExecutionInfo signalExecution, IList <SmartSignalResultItemPresentation> smartSignalResultItems)
        {
            AlertRule alertRule = signalExecution.AlertRule;

            if (this.sendGridClient == null)
            {
                this.tracer.TraceWarning("SendGrid API key was not found, not sending email");
                return;
            }

            if (alertRule.EmailRecipients == null || !alertRule.EmailRecipients.Any())
            {
                this.tracer.TraceWarning("Email recipients were not provided, not sending email");
                return;
            }

            if (smartSignalResultItems == null || !smartSignalResultItems.Any())
            {
                this.tracer.TraceInformation($"no result items to publish for signal {alertRule.SignalId}");
                return;
            }

            this.tracer.TraceInformation($"Sending signal result email for signal {alertRule.SignalId}");

            var exceptions = new List <Exception>();

            foreach (SmartSignalResultItemPresentation resultItem in smartSignalResultItems)
            {
                ResourceIdentifier resource = ResourceIdentifier.CreateFromResourceId(alertRule.ResourceId);

                // TODO: Fix links
                string emailBody = Resources.SmartSignalEmailTemplate
                                   .Replace(SignalNamePlaceHolder, resultItem.SignalName)
                                   .Replace(ResourceNamePlaceHolder, resultItem.ResourceId)
                                   .Replace(LinkToPortalPlaceHolder, "LinkToPortal")
                                   .Replace(RuleNamePlaceHolder, alertRule.Name)
                                   .Replace(RuleDescriptionPlaceHolder, alertRule.Description)
                                   .Replace(ServiceNamePlaceHolder, $@"{resource.ResourceType}: {resource.ResourceName} ({resource.ResourceGroupName})")
                                   .Replace(AlertActivatedTimePlaceHolder, signalExecution.LastExecutionTime.ToString())
                                   .Replace(SubscriptionNamePlaceHolder, resource.SubscriptionId)
                                   .Replace(LinkToFeedbackPlaceHolder, "https://ms.portal.azure.com/");

                var msg = new SendGridMessage
                {
                    From             = new EmailAddress("*****@*****.**", "Smart Signals"),
                    Subject          = $"Azure Smart Alerts (preview) - {resultItem.SignalName} detected",
                    PlainTextContent = $@"{resultItem.SignalName} was detected for {resultItem.ResourceId}. 
                                        You can view more details for this alert here: {"LinkToPortal"}",
                    HtmlContent      = emailBody
                };

                var emailAddresses = alertRule.EmailRecipients.Select(email => new EmailAddress(email)).ToList();
                msg.AddTos(emailAddresses);

                try
                {
                    var response = await this.sendGridClient.SendEmailAsync(msg);

                    if (!IsSuccessStatusCode(response.StatusCode))
                    {
                        string content = response.Body != null ? await response.Body.ReadAsStringAsync() : string.Empty;

                        var message = $"Failed to send signal results Email for signal {alertRule.SignalId}. Fail StatusCode: {response.StatusCode}. Content: {content}.";
                        this.tracer.TraceError(message);
                        exceptions.Add(new EmailSendingException(message));
                    }
                }
                catch (Exception e)
                {
                    this.tracer.TraceError($"Failed to send email. Exception: {e}");
                    exceptions.Add(new EmailSendingException($"Exception was thrown fo sending signal results Email for signal {alertRule.SignalId}. Exception: {e}"));
                }
            }

            if (exceptions.Count > 0)
            {
                this.tracer.TraceError(
                    $"Failed to send one or more signal result emails." +
                    $"Number of exceptions thrown: {exceptions.Count()}.");
                throw new AggregateException("Failed to send one or more signal result emails", exceptions);
            }

            this.tracer.TraceInformation($"Sent signal result emails successfully for signal {alertRule.SignalId}");
        }
        /// <summary>
        /// Runs the Smart Detector's resolution check flow.
        /// </summary>
        /// <param name="request">The alert resolution check 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 resolution check response generated by the Smart Detector.</returns>
        private async Task <ContractsAlertResolutionCheckResponse> CheckAlertResolutionAsync(
            ContractsAlertResolutionCheckRequest request,
            ISmartDetector smartDetector,
            SmartDetectorManifest smartDetectorManifest,
            ITracer detectorTracer,
            CancellationToken cancellationToken)
        {
            // Check that the detector supports resolution
            if (!(smartDetector is IResolvableAlertSmartDetector resolvableAlertSmartDetector))
            {
                throw new ResolutionCheckNotSupportedException($"Smart Detector {smartDetectorManifest.Name} does not support alert resolution of alerts");
            }

            // Create state repository
            IStateRepository stateRepository = this.stateRepositoryFactory.Create(request.OriginalAnalysisRequest.SmartDetectorId, request.OriginalAnalysisRequest.AlertRuleResourceId);

            // Load the resolution state from the repository
            ResolutionState resolutionState = await stateRepository.GetStateAsync <ResolutionState>(GetResolutionStateKey(request.AlertCorrelationHash), cancellationToken);

            if (resolutionState == null)
            {
                throw new ResolutionStateNotFoundException($"Resolution state for Alert with correlation {request.AlertCorrelationHash} was not found");
            }

            // Create the input for the Smart Detector
            AnalysisRequestParameters analysisRequestParameters = await this.CreateAnalysisRequestParametersAsync(
                resolutionState.AnalysisRequestTime,
                request.OriginalAnalysisRequest,
                smartDetectorManifest,
                false,
                cancellationToken);

            var alertResolutionCheckRequest = new AlertResolutionCheckRequest(
                analysisRequestParameters,
                new AlertResolutionCheckRequestParameters(ResourceIdentifier.CreateFromResourceId(request.TargetResource), request.AlertFireTime, resolutionState.AlertPredicates),
                this.analysisServicesFactory,
                stateRepository);

            // Run the Smart Detector
            this.tracer.TraceInformation($"Started running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name} for resolution check");
            try
            {
                AlertResolutionCheckResponse alertResolutionCheckResponse = await resolvableAlertSmartDetector.CheckForResolutionAsync(alertResolutionCheckRequest, detectorTracer, cancellationToken);

                this.tracer.TraceInformation($"Completed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name} for resolution check");

                // If the alert is resolved - delete the state
                if (alertResolutionCheckResponse.ShouldBeResolved)
                {
                    await stateRepository.DeleteStateAsync(GetResolutionStateKey(request.AlertCorrelationHash), cancellationToken);
                }

                // Convert the result
                return(new ContractsAlertResolutionCheckResponse
                {
                    ShouldBeResolved = alertResolutionCheckResponse.ShouldBeResolved,
                    ResolutionParameters = alertResolutionCheckResponse.AlertResolutionParameters?.CreateContractsResolutionParameters()
                });
            }
            catch (Exception e)
            {
                this.tracer.TraceError($"Failed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name} for resolution check: {e}");
                throw new FailedToRunSmartDetectorException($"Calling Smart Detector '{smartDetectorManifest.Name}' for resolution check failed with exception of type {e.GetType()} and message: {e.Message}", e);
            }
        }
        /// <summary>
        /// Read the metric values
        /// </summary>
        /// <returns>A <see cref="Task"/>, running the current operation, returning the metric values as a <see cref="LineSeries"/></returns>
        private async Task <ChartValues <DateTimePoint> > ReadChartValuesAsync()
        {
            CancellationToken cancellationToken = CancellationToken.None;

            // Verify start/end times
            DateTime startTime = this.metricChartAlertProperty.StartTimeUtc ?? throw new ApplicationException("Start time cannot be null");
            DateTime endTime   = this.metricChartAlertProperty.EndTimeUtc ?? throw new ApplicationException("End time cannot be null");

            if (endTime > DateTime.UtcNow)
            {
                endTime = DateTime.UtcNow;
            }

            // Convert from aggregation type to aggregation
            Aggregation aggregation;

            switch (this.metricChartAlertProperty.AggregationType)
            {
            case AggregationType.Average:
                aggregation = Aggregation.Average;
                break;

            case AggregationType.Count:
                aggregation = Aggregation.Count;
                break;

            case AggregationType.Sum:
                aggregation = Aggregation.Total;
                break;

            case AggregationType.Maximum:
                aggregation = Aggregation.Maximum;
                break;

            case AggregationType.Minimum:
                aggregation = Aggregation.Minimum;
                break;

            default:
                throw new ApplicationException($"Invalid aggregation type {this.metricChartAlertProperty.AggregationType}");
            }

            // Create the metrics client
            ResourceIdentifier resource     = ResourceIdentifier.CreateFromResourceId(this.metricChartAlertProperty.ResourceId);
            IMetricClient      metricClient = await this.analysisServicesFactory.CreateMetricClientAsync(resource.SubscriptionId, cancellationToken)
                                              .ConfigureAwait(false);

            // Send a metric query using the metric client
            IEnumerable <MetricQueryResult> metricQueryResults = await metricClient.GetResourceMetricsAsync(
                this.metricChartAlertProperty.ResourceId,
                new QueryParameters()
            {
                MetricNamespace = this.metricChartAlertProperty.MetricNamespace,
                MetricNames     = new List <string>()
                {
                    this.metricChartAlertProperty.MetricName
                },
                StartTime    = startTime,
                EndTime      = endTime,
                Interval     = this.metricChartAlertProperty.TimeGrain,
                Aggregations = new List <Aggregation>()
                {
                    aggregation
                },
            },
                cancellationToken)
                                                                 .ConfigureAwait(false);

            // Get chart points
            ChartValues <DateTimePoint> values = new ChartValues <DateTimePoint>(
                metricQueryResults
                .Single()
                .Timeseries
                .Single()
                .Data
                .Select(p => new DateTimePoint(p.TimeStamp, p.GetValue(aggregation))));

            return(values);
        }