/// <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(); } }
/// <summary> /// Perform basic validations on the specified query run information. /// </summary> /// <param name="runInfo">The query run information</param> /// <param name="expectedType">The expected telemetry DB type</param> private void VerifyRunInfo(SmartSignalResultItemQueryRunInfo runInfo, TelemetryDbType expectedType) { // Verify the telemetry DB type if (runInfo.Type != expectedType) { throw new TelemetryDataClientCreationException($"Telemetry client creation failed - telemetry resource type is {runInfo.Type}"); } // Verify that the resource IDs are not empty if (!runInfo.ResourceIds.Any()) { throw new TelemetryDataClientCreationException("Telemetry client creation failed - no resources found"); } }
/// <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 SmartSignalResultItemQueryRunInfo 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 SmartSignalResultItemQueryRunInfo 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 async Task WhenCreatingQueryRunInfoForApplicationInsightsResourcesThenTheCorrectInfoIsCreated() { var resources = new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.ApplicationInsights, SubscriptionId, ResourceGroupName, ResourceName) }; IQueryRunInfoProvider provider = new QueryRunInfoProvider(this.azureResourceManagerClientMock.Object); SmartSignalResultItemQueryRunInfo queryRunInfo = await provider.GetQueryRunInfoAsync(resources, default(CancellationToken)); Assert.IsNotNull(queryRunInfo, "Query run information is null"); Assert.AreEqual(TelemetryDbType.ApplicationInsights, queryRunInfo.Type, "Wrong telemetry DB type"); CollectionAssert.AreEqual(new[] { resources.First().ToResourceId() }, queryRunInfo.ResourceIds.ToArray(), "Wrong resource IDs"); }
public async Task WhenCreatingQueryRunInfoForLogAnalyticsResourcesThenTheCorrectInfoIsCreated() { var resources = new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.LogAnalytics, SubscriptionId, ResourceGroupName, WorkspaceNames[0]), new ResourceIdentifier(ResourceType.LogAnalytics, SubscriptionId, ResourceGroupName, WorkspaceNames[1]) }; string[] resourceIds = resources.Select(r => r.ToResourceId()).ToArray(); IQueryRunInfoProvider provider = new QueryRunInfoProvider(this.azureResourceManagerClientMock.Object); SmartSignalResultItemQueryRunInfo queryRunInfo = await provider.GetQueryRunInfoAsync(resources, default(CancellationToken)); Assert.IsNotNull(queryRunInfo, "Query run information is null"); Assert.AreEqual(TelemetryDbType.LogAnalytics, queryRunInfo.Type, "Wrong telemetry DB type"); CollectionAssert.AreEqual(resourceIds, queryRunInfo.ResourceIds.ToArray(), "Wrong resource IDs"); }
public async Task WhenCreatingQueryRunInfoForResourcesWithMultipleSubscriptionsThenAllWorkspacesAreReturned() { var resources = new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.VirtualMachine, SubscriptionId + "1", ResourceGroupName + "1", ResourceName + "1"), new ResourceIdentifier(ResourceType.VirtualMachine, SubscriptionId + "2", ResourceGroupName + "2", ResourceName + "2") }; var workspaces = new List <ResourceIdentifier>() { new ResourceIdentifier(ResourceType.LogAnalytics, SubscriptionId + "1", ResourceGroupName + "1", ResourceName + "1"), new ResourceIdentifier(ResourceType.LogAnalytics, SubscriptionId + "2", ResourceGroupName + "2", ResourceName + "2"), new ResourceIdentifier(ResourceType.LogAnalytics, SubscriptionId + "2", ResourceGroupName + "2", ResourceName + "3") }; this.azureResourceManagerClientMock .Setup(x => x.GetAllResourcesInSubscriptionAsync(SubscriptionId + "1", It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>())) .ReturnsAsync(new List <ResourceIdentifier>() { workspaces[0] }); this.azureResourceManagerClientMock .Setup(x => x.GetAllResourcesInSubscriptionAsync(SubscriptionId + "2", It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>())) .ReturnsAsync(new List <ResourceIdentifier>() { workspaces[1], workspaces[2] }); this.azureResourceManagerClientMock .Setup(x => x.GetLogAnalyticsWorkspaceIdAsync(It.Is <ResourceIdentifier>(identifier => identifier.ResourceType == ResourceType.LogAnalytics), It.IsAny <CancellationToken>())) .ReturnsAsync("workspaceId"); IQueryRunInfoProvider provider = new QueryRunInfoProvider(this.azureResourceManagerClientMock.Object); SmartSignalResultItemQueryRunInfo queryRunInfo = await provider.GetQueryRunInfoAsync(resources, default(CancellationToken)); Assert.IsNotNull(queryRunInfo, "Query run information is null"); this.azureResourceManagerClientMock.Verify(x => x.GetAllResourcesInSubscriptionAsync(SubscriptionId + "1", It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>()), Times.Once); this.azureResourceManagerClientMock.Verify(x => x.GetAllResourcesInSubscriptionAsync(SubscriptionId + "2", It.IsAny <IEnumerable <ResourceType> >(), It.IsAny <CancellationToken>()), Times.Once); CollectionAssert.AreEqual(workspaces.Select(w => w.ToResourceId()).ToArray(), queryRunInfo.ResourceIds.ToArray()); }
private SmartSignalResultItemPresentation CreatePresentation(SmartSignalResultItem resultItem, bool nullQueryRunInfo = false) { SmartSignalResultItemQueryRunInfo queryRunInfo = null; if (!nullQueryRunInfo) { queryRunInfo = new SmartSignalResultItemQueryRunInfo( resultItem.ResourceIdentifier.ResourceType == ResourceType.ApplicationInsights ? TelemetryDbType.ApplicationInsights : TelemetryDbType.LogAnalytics, new List <string>() { "resourceId1", "resourceId2" }); } DateTime lastExecutionTime = DateTime.Now.Date.AddDays(-1); string resourceId = "resourceId"; var request = new SmartSignalRequest(new List <string>() { resourceId }, "signalId", lastExecutionTime, TimeSpan.FromDays(1), new SmartSignalSettings()); return(SmartSignalResultItemPresentation.CreateFromResultItem(request, SignalName, resultItem, queryRunInfo)); }
/// <summary> /// Loads the signal, runs it, and returns the generated result presentations /// </summary> /// <param name="request">The signal request</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A <see cref="Task{TResult}"/>, returning the list of Smart Signal result item presentations generated by the signal</returns> public async Task <List <SmartSignalResultItemPresentation> > RunAsync(SmartSignalRequest request, CancellationToken cancellationToken) { // Read the signal's package this.tracer.TraceInformation($"Loading signal package for signal ID {request.SignalId}"); SmartSignalPackage signalPackage = await this.smartSignalRepository.ReadSignalPackageAsync(request.SignalId, cancellationToken); SmartSignalManifest signalManifest = signalPackage.Manifest; this.tracer.TraceInformation($"Read signal package, ID {signalManifest.Id}, Version {signalManifest.Version}"); // Load the signal ISmartSignal signal = this.smartSignalLoader.LoadSignal(signalPackage); this.tracer.TraceInformation($"Signal instance loaded successfully, ID {signalManifest.Id}"); // Get the resources on which to run the signal List <ResourceIdentifier> resources = await this.GetResourcesForSignal(request.ResourceIds, signalManifest, cancellationToken); // Run the signal this.tracer.TraceInformation($"Started running signal ID {signalManifest.Id}, Name {signalManifest.Name}"); SmartSignalResult signalResult; try { var analysisRequest = new AnalysisRequest(resources, request.LastExecutionTime, request.Cadence, this.analysisServicesFactory); signalResult = await signal.AnalyzeResourcesAsync(analysisRequest, this.tracer, cancellationToken); this.tracer.TraceInformation($"Completed running signal ID {signalManifest.Id}, Name {signalManifest.Name}, returning {signalResult.ResultItems.Count} result items"); } catch (Exception e) { this.tracer.TraceInformation($"Failed running signal ID {signalManifest.Id}, Name {signalManifest.Name}: {e.Message}"); throw new SmartSignalCustomException(e.GetType().ToString(), e.Message, e.StackTrace); } // Verify that each result item belongs to one of the types declared in the signal manifest foreach (SmartSignalResultItem resultItem in signalResult.ResultItems) { if (!signalManifest.SupportedResourceTypes.Contains(resultItem.ResourceIdentifier.ResourceType)) { throw new UnidentifiedResultItemResourceTypeException(resultItem.ResourceIdentifier); } } // Trace the number of result items of each type foreach (var resultItemType in signalResult.ResultItems.GroupBy(x => x.GetType().Name)) { this.tracer.TraceInformation($"Got {resultItemType.Count()} Smart Signal result items of type '{resultItemType.Key}'"); this.tracer.ReportMetric("SignalResultItemType", resultItemType.Count(), new Dictionary <string, string>() { { "ResultItemType", resultItemType.Key } }); } // Create results List <SmartSignalResultItemPresentation> results = new List <SmartSignalResultItemPresentation>(); foreach (var resultItem in signalResult.ResultItems) { SmartSignalResultItemQueryRunInfo queryRunInfo = await this.queryRunInfoProvider.GetQueryRunInfoAsync(new List <ResourceIdentifier>() { resultItem.ResourceIdentifier }, cancellationToken); results.Add(SmartSignalResultItemPresentation.CreateFromResultItem(request, signalManifest.Name, resultItem, queryRunInfo)); } this.tracer.TraceInformation($"Returning {results.Count} results"); return(results); }