private async Task TestLoadSignalSimple(Type signalType, string expectedTitle = "test test test") { ISmartSignalLoader loader = new SmartSignalLoader(this.tracerMock.Object); SmartSignalManifest manifest = new SmartSignalManifest("3", "simple", "description", Version.Parse("1.0"), signalType.Assembly.GetName().Name, signalType.FullName, new List <ResourceType>() { ResourceType.Subscription }, new List <int> { 60 }); SmartSignalPackage package = new SmartSignalPackage(manifest, this.assemblies["3"]); ISmartSignal signal = loader.LoadSignal(package); Assert.IsNotNull(signal, "Signal is NULL"); var resource = new ResourceIdentifier(ResourceType.VirtualMachine, "someSubscription", "someGroup", "someVM"); var analysisRequest = new AnalysisRequest( new List <ResourceIdentifier> { resource }, DateTime.UtcNow.AddDays(-1), TimeSpan.FromDays(1), new Mock <IAnalysisServicesFactory>().Object); SmartSignalResult signalResult = await signal.AnalyzeResourcesAsync(analysisRequest, this.tracerMock.Object, default(CancellationToken)); Assert.AreEqual(1, signalResult.ResultItems.Count, "Incorrect number of result items returned"); Assert.AreEqual(expectedTitle, signalResult.ResultItems.Single().Title, "Result item title is wrong"); Assert.AreEqual(resource, signalResult.ResultItems.Single().ResourceIdentifier, "Result item resource identifier is wrong"); }
public SignalConfigurationControlViewModel( IAzureResourceManagerClient azureResourceManagerClient, ITracer tracer, SmartSignalManifest smartSignalManifest, SmartSignalRunner signalRunner) { this.azureResourceManagerClient = azureResourceManagerClient; this.smartSignalManifes = smartSignalManifest; this.tracer = tracer; this.SignalRunner = signalRunner; this.SignalName = this.smartSignalManifes.Name; this.ShouldShowStatusControl = false; // Initialize cadences combo box IEnumerable <SignalCadence> cadences = this.smartSignalManifes.SupportedCadencesInMinutes .Select(cadence => new SignalCadence(TimeSpan.FromMinutes(cadence))); this.Cadences = new ObservableCollection <SignalCadence>(cadences); // Initialize combo boxes read tasks this.ReadSubscriptionsTask = new ObservableTask <ObservableCollection <AzureSubscription> >( this.GetSubscriptionsAsync()); this.ReadResourceGroupsTask = new ObservableTask <ObservableCollection <string> >( Task.FromResult(new ObservableCollection <string>())); this.ReadResourceTypesTask = new ObservableTask <ObservableCollection <string> >( Task.FromResult(new ObservableCollection <string>())); this.ReadResourcesTask = new ObservableTask <ObservableCollection <ResourceIdentifier> >( Task.FromResult(new ObservableCollection <ResourceIdentifier>())); }
/// <summary> /// Loads a Smart Signal. /// This method load the signal's assembly into the current application domain, /// and creates the signal object using reflection. /// </summary> /// <param name="signalPackage">The signal package.</param> /// <returns>The Smart Signal object.</returns> /// <exception cref="SmartSignalLoadException"> /// Thrown if an error occurred during the signal load (either due to assembly load /// error or failure to create the signal object). /// </exception> public ISmartSignal LoadSignal(SmartSignalPackage signalPackage) { SmartSignalManifest signalManifest = signalPackage.Manifest; IReadOnlyDictionary <string, byte[]> signalAssemblies = signalPackage.Content; try { this.tracer.TraceInformation($"Read {signalAssemblies.Count} assemblies for signal ID {signalManifest.Id}"); // Add assembly resolver, that uses the signal's assemblies AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { this.tracer.TraceInformation($"Resolving assembly {args.Name} for signal ID {signalManifest.Id}"); // Get the short name of the assembly (AssemblyName.Name) AssemblyName assemblyName = new AssemblyName(args.Name); string name = assemblyName.Name; // Try to find the assembly bytes in the signal's assemblies if (signalAssemblies.TryGetValue(name, out byte[] assemblyBytes)) { // Load the assembly from its bytes return(Assembly.Load(assemblyBytes)); } return(null); }; // Find the main signal assembly if (!signalAssemblies.TryGetValue(signalManifest.AssemblyName, out byte[] signalMainAssemblyBytes))
private void AssertMetadata(SmartSignalManifest signalManifest, Dictionary <string, string> expectedMetadata) { var supportedResourceTypes = JArray.Parse(expectedMetadata["supportedResourceTypes"]) .Select(jtoken => (ResourceType)Enum.Parse(typeof(ResourceType), jtoken.ToString(), true)) .ToList(); var supportedCadencesInMinutes = JArray.Parse(expectedMetadata["supportedCadencesInMinutes"]) .Select(jToken => int.Parse(jToken.ToString())) .ToList(); Assert.AreEqual(expectedMetadata["id"], signalManifest.Id); Assert.AreEqual(expectedMetadata["name"], signalManifest.Name); Assert.AreEqual(expectedMetadata["version"], signalManifest.Version.ToString()); Assert.AreEqual(expectedMetadata["description"], signalManifest.Description); Assert.AreEqual(expectedMetadata["assemblyName"], signalManifest.AssemblyName); Assert.AreEqual(expectedMetadata["className"], signalManifest.ClassName); Assert.AreEqual(supportedResourceTypes.Count, signalManifest.SupportedResourceTypes.Count); foreach (var supportedResourceType in supportedResourceTypes) { Assert.IsTrue(signalManifest.SupportedResourceTypes.Contains(supportedResourceType)); } Assert.AreEqual(supportedCadencesInMinutes.Count, signalManifest.SupportedCadencesInMinutes.Count); foreach (var supportedCadence in supportedCadencesInMinutes) { Assert.IsTrue(signalManifest.SupportedCadencesInMinutes.Contains(supportedCadence)); } }
/// <summary> /// Raises the <see cref="E:System.Windows.Application.Startup" /> event. /// </summary> /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the event data.</param> protected override void OnStartup(StartupEventArgs e) { ITracer stringTracer = new StringTracer(string.Empty); ITracer consoleTracer = new ConsoleTracer(string.Empty); var signalLoader = new SmartSignalLoader(consoleTracer); // *Temporary*: if package file path wasn't accepted, raise file selection window to allow package file selection. // This option should be removed before launching version for customers (bug for tracking: 1177247) string signalPackagePath = e.Args.Length != 1 ? this.GetSignalPackagePath() : Diagnostics.EnsureStringNotNullOrWhiteSpace(() => e.Args[0]); SmartSignalPackage signalPackage; using (var fileStream = new FileStream(signalPackagePath, FileMode.Open)) { signalPackage = SmartSignalPackage.CreateFromStream(fileStream, consoleTracer); } SmartSignalManifest signalManifest = signalPackage.Manifest; ISmartSignal signal = signalLoader.LoadSignal(signalPackage); // Authenticate the user to Active Directory var authenticationServices = new AuthenticationServices(); authenticationServices.AuthenticateUser(); ICredentialsFactory credentialsFactory = new ActiveDirectoryCredentialsFactory(authenticationServices); IAzureResourceManagerClient azureResourceManagerClient = new AzureResourceManagerClient(credentialsFactory, consoleTracer); // Create analysis service factory var queryRunInroProvider = new QueryRunInfoProvider(azureResourceManagerClient); var httpClientWrapper = new HttpClientWrapper(); IAnalysisServicesFactory analysisServicesFactory = new AnalysisServicesFactory(consoleTracer, httpClientWrapper, credentialsFactory, azureResourceManagerClient, queryRunInroProvider); var signalRunner = new SmartSignalRunner(signal, analysisServicesFactory, queryRunInroProvider, signalManifest, stringTracer); // Create a Unity container with all the required models and view models registrations Container = new UnityContainer(); Container .RegisterInstance(stringTracer) .RegisterInstance(new SignalsResultsRepository()) .RegisterInstance(authenticationServices) .RegisterInstance(azureResourceManagerClient) .RegisterInstance(signal) .RegisterInstance(signalManifest) .RegisterInstance(analysisServicesFactory) .RegisterInstance(signalRunner); }
/// <summary> /// Initializes a new instance of the <see cref="SmartSignalRunner"/> class. /// </summary> /// <param name="smartSignal">The smart signal.</param> /// <param name="analysisServicesFactory">The analysis services factory.</param> /// <param name="queryRunInfoProvider">The query run information provider.</param> /// <param name="smartSignalManifest">The smart signal manifest.</param> /// <param name="tracer">The tracer.</param> public SmartSignalRunner( ISmartSignal smartSignal, IAnalysisServicesFactory analysisServicesFactory, IQueryRunInfoProvider queryRunInfoProvider, SmartSignalManifest smartSignalManifest, ITracer tracer) { this.smartSignal = smartSignal; this.analysisServicesFactory = analysisServicesFactory; this.queryRunInfoProvider = queryRunInfoProvider; this.smartSignalManifes = smartSignalManifest; this.Tracer = tracer; this.IsSignalRunning = false; this.Results = new ObservableCollection <SignalResultItem>(); }
/// <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); }
/// <summary> /// Verify that the request resource type is supported by the signal, and enumerate /// the resources that the signal should run on. /// </summary> /// <param name="requestResourceIds">The request resource Ids</param> /// <param name="smartSignalManifest">The signal manifest</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A <see cref="Task{TResult}"/>, returning the resource identifiers that the signal should run on</returns> private async Task <List <ResourceIdentifier> > GetResourcesForSignal(IList <string> requestResourceIds, SmartSignalManifest smartSignalManifest, CancellationToken cancellationToken) { HashSet <ResourceIdentifier> resourcesForSignal = new HashSet <ResourceIdentifier>(); foreach (string requestResourceId in requestResourceIds) { ResourceIdentifier requestResource = ResourceIdentifier.CreateFromResourceId(requestResourceId); if (smartSignalManifest.SupportedResourceTypes.Contains(requestResource.ResourceType)) { // If the signal directly supports the requested resource type, then that's it resourcesForSignal.Add(requestResource); } else if (requestResource.ResourceType == ResourceType.Subscription && smartSignalManifest.SupportedResourceTypes.Contains(ResourceType.ResourceGroup)) { // If the request is for a subscription, and the signal supports a resource group type, enumerate all resource groups in the requested subscription IList <ResourceIdentifier> resourceGroups = await this.azureResourceManagerClient.GetAllResourceGroupsInSubscriptionAsync(requestResource.SubscriptionId, cancellationToken); resourcesForSignal.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 signal supports IList <ResourceIdentifier> resources = await this.azureResourceManagerClient.GetAllResourcesInSubscriptionAsync(requestResource.SubscriptionId, smartSignalManifest.SupportedResourceTypes, cancellationToken); resourcesForSignal.UnionWith(resources); this.tracer.TraceInformation($"Added {resources.Count} resources found in subscription {requestResource.SubscriptionId}"); } else if (requestResource.ResourceType == ResourceType.ResourceGroup && smartSignalManifest.SupportedResourceTypes.Any(type => type != ResourceType.Subscription)) { // If the request is for a resource group, and the signal supports resource types (other than subscription), // enumerate all the resources in the requested resource group that the signal supports IList <ResourceIdentifier> resources = await this.azureResourceManagerClient.GetAllResourcesInResourceGroupAsync(requestResource.SubscriptionId, requestResource.ResourceGroupName, smartSignalManifest.SupportedResourceTypes, cancellationToken); resourcesForSignal.UnionWith(resources); this.tracer.TraceInformation($"Added {resources.Count} resources found in the specified resource group in subscription {requestResource.SubscriptionId}"); } else { // The signal does not support the requested resource type throw new IncompatibleResourceTypesException(requestResource.ResourceType, smartSignalManifest); } } return(resourcesForSignal.ToList()); }
private void TestInitialize(ResourceType requestResourceType, ResourceType signalResourceType) { this.tracerMock = new Mock <ITracer>(); 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.request = new SmartSignalRequest(this.resourceIds, "1", DateTime.UtcNow.AddDays(-1), TimeSpan.FromDays(1), new SmartSignalSettings()); var smartSignalManifest = new SmartSignalManifest("1", "Test signal", "Test signal description", Version.Parse("1.0"), "assembly", "class", new List <ResourceType>() { signalResourceType }, new List <int> { 60 }); this.smartSignalPackage = new SmartSignalPackage(smartSignalManifest, new Dictionary <string, byte[]> { ["TestSignalLibrary"] = new byte[0] }); this.smartSignalsRepositoryMock = new Mock <ISmartSignalRepository>(); this.smartSignalsRepositoryMock .Setup(x => x.ReadSignalPackageAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .ReturnsAsync(() => this.smartSignalPackage); this.analysisServicesFactoryMock = new Mock <IAnalysisServicesFactory>(); this.signal = new TestSignal { ExpectedResourceType = signalResourceType }; this.smartSignalLoaderMock = new Mock <ISmartSignalLoader>(); this.smartSignalLoaderMock .Setup(x => x.LoadSignal(this.smartSignalPackage)) .Returns(this.signal); this.azureResourceManagerClientMock = new Mock <IAzureResourceManagerClient>(); this.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) }); this.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") }); this.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.queryRunInfoProviderMock = new Mock <IQueryRunInfoProvider>(); }
/// <summary> /// Initializes a new instance of the <see cref="IncompatibleResourceTypesException"/> class /// with the specified error message. /// </summary> /// <param name="requestResourceType">The requested resource type</param> /// <param name="smartSignalManifest">The signal manifest</param> public IncompatibleResourceTypesException(ResourceType requestResourceType, SmartSignalManifest smartSignalManifest) : base($"Resource type {requestResourceType} is not supported by signal {smartSignalManifest.Name}") { }