sources/Google.Solutions.IapDesktop.Extensions.Management/ToolWindows/EventLog/EventLogViewModel.cs (272 lines of code) (raw):

// // Copyright 2020 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using Google.Solutions.Common.Diagnostics; using Google.Solutions.Common.Threading; using Google.Solutions.IapDesktop.Application; using Google.Solutions.IapDesktop.Application.Client; using Google.Solutions.IapDesktop.Application.Windows; using Google.Solutions.IapDesktop.Core.ObjectModel; using Google.Solutions.IapDesktop.Core.ProjectModel; using Google.Solutions.IapDesktop.Extensions.Management.Auditing.Events; using Google.Solutions.Mvvm.Binding; using Google.Solutions.Mvvm.Cache; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Google.Solutions.IapDesktop.Extensions.Management.ToolWindows.EventLog { [Service] public class EventLogViewModel : ModelCachingViewModelBase<IProjectModelNode, EventLogModel> { private const int ModelCacheCapacity = 5; internal const string DefaultWindowTitle = "Event log"; public static readonly ReadOnlyCollection<Timeframe> AvailableTimeframes = new ReadOnlyCollection<Timeframe>(new List<Timeframe>() { new Timeframe(TimeSpan.FromDays(7), "Last 7 days"), new Timeframe(TimeSpan.FromDays(14), "Last 14 days"), new Timeframe(TimeSpan.FromDays(30), "Last 30 days") }); private readonly ICloudConsoleClient cloudConsoleAdapter; private readonly IJobService jobService; private readonly Service<IAuditLogClient> auditLogAdapter; private EventBase? selectedEvent; private int selectedTimeframeIndex = 0; private bool isEventListEnabled = false; private bool isRefreshButtonEnabled = false; private bool isTimeframeComboBoxEnabled = false; private bool includeSystemEvents = true; private bool includeLifecycleEvents = true; private bool includeAccessEvents = true; private string windowTitle = DefaultWindowTitle; private Timeframe SelectedTimeframe => AvailableTimeframes[this.selectedTimeframeIndex]; public EventLogViewModel(IServiceProvider serviceProvider) : base(ModelCacheCapacity) { this.cloudConsoleAdapter = serviceProvider.GetService<ICloudConsoleClient>(); this.jobService = serviceProvider.GetService<IJobService>(); this.auditLogAdapter = serviceProvider.GetService<Service<IAuditLogClient>>(); this.Events = new RangeObservableCollection<EventBase>(); } //--------------------------------------------------------------------- // Observable properties. //--------------------------------------------------------------------- public EventBase? SelectedEvent { get => this.selectedEvent; set { this.selectedEvent = value; RaisePropertyChange(); RaisePropertyChange((EventLogViewModel m) => m.IsOpenSelectedEventInCloudConsoleButtonEnabled); } } public bool IsOpenSelectedEventInCloudConsoleButtonEnabled { get => this.selectedEvent != null; } public bool IsEventListEnabled { get => this.isEventListEnabled; set { this.isEventListEnabled = value; RaisePropertyChange(); } } public bool IsRefreshButtonEnabled { get => this.isRefreshButtonEnabled; set { this.isRefreshButtonEnabled = value; RaisePropertyChange(); } } public bool IsTimeframeComboBoxEnabled { get => this.isTimeframeComboBoxEnabled; set { this.isTimeframeComboBoxEnabled = value; RaisePropertyChange(); } } public RangeObservableCollection<EventBase> Events { get; } public int SelectedTimeframeIndex { get => this.selectedTimeframeIndex; set { this.selectedTimeframeIndex = value; RaisePropertyChange(); // Reload from backend. _ = InvalidateAsync().ConfigureAwait(true); } } public bool IsIncludeSystemEventsButtonChecked { get => this.includeSystemEvents; set { this.includeSystemEvents = value; RaisePropertyChange(); // Reapply filters. ApplyModel(true); } } public bool IsIncludeLifecycleEventsButtonChecked { get => this.includeLifecycleEvents; set { this.includeLifecycleEvents = value; RaisePropertyChange(); // Reapply filters. ApplyModel(true); } } public bool IsIncludeAccessEventsButtonChecked { get => this.includeAccessEvents; set { this.includeAccessEvents = value; RaisePropertyChange(); // Reapply filters. ApplyModel(true); } } public string WindowTitle { get => this.windowTitle; set { this.windowTitle = value; RaisePropertyChange(); } } //--------------------------------------------------------------------- // Actions. //--------------------------------------------------------------------- public void Refresh() { _ = InvalidateAsync().ConfigureAwait(true); } public void OpenSelectedEventInCloudConsole() { if (this.SelectedEvent != null && this.SelectedEvent.LogRecord.InsertId != null) { this.cloudConsoleAdapter.OpenVmInstanceLogDetails( this.SelectedEvent.LogRecord.ProjectId, this.SelectedEvent.LogRecord.InsertId, this.SelectedEvent.Timestamp); } } public void OpenInCloudConsole() { Debug.Assert(!(this.ModelKey is IProjectModelCloudNode)); if (this.ModelKey != null) { this.cloudConsoleAdapter.OpenLogs(this.ModelKey); } } //--------------------------------------------------------------------- // ModelCachingViewModelBase. //--------------------------------------------------------------------- protected override async Task<EventLogModel?> LoadModelAsync( IProjectModelNode node, CancellationToken token) { using (ApplicationTraceSource.Log.TraceMethod().WithParameters(node)) { IEnumerable<ulong>? instanceIdFilter; IEnumerable<string>? zonesFilter; string projectIdFilter; string displayName; if (node is IProjectModelInstanceNode vmNode) { displayName = vmNode.Instance.Name; instanceIdFilter = new[] { vmNode.InstanceId }; zonesFilter = null; projectIdFilter = vmNode.Instance.ProjectId; } else if (node is IProjectModelZoneNode zoneNode) { displayName = zoneNode.Zone.Name; instanceIdFilter = null; zonesFilter = new[] { zoneNode.Zone.Name }; projectIdFilter = zoneNode.Zone.ProjectId; } else if (node is IProjectModelProjectNode projectNode) { displayName = projectNode.Project.ProjectId; instanceIdFilter = null; zonesFilter = null; projectIdFilter = projectNode.Project.ProjectId; } else { // Unknown/unsupported node. return null; } this.IsRefreshButtonEnabled = this.IsTimeframeComboBoxEnabled = false; try { // Load data using a job so that the task is retried in case // of authentication issues. return await this.jobService.RunAsync( new JobDescription( $"Loading logs for {displayName}", JobUserFeedbackType.BackgroundFeedback), async jobToken => { using (var combinedTokenSource = jobToken.Combine(token)) { var model = new EventLogModel(displayName); await this.auditLogAdapter .Activate() .ProcessInstanceEventsAsync( new[] { projectIdFilter }, zonesFilter, instanceIdFilter, DateTime.UtcNow.Subtract(this.SelectedTimeframe.Duration), model, combinedTokenSource.Token) .ConfigureAwait(false); return model; } }).ConfigureAwait(true); // Back to original (UI) thread. } finally { this.IsRefreshButtonEnabled = this.IsTimeframeComboBoxEnabled = true; } } } protected override void ApplyModel(bool cached) { this.Events.Clear(); this.SelectedEvent = null; if (this.Model == null) { // Unsupported node. this.IsEventListEnabled = false; this.WindowTitle = DefaultWindowTitle; } else { this.IsEventListEnabled = true; this.WindowTitle = DefaultWindowTitle + $": {this.Model.DisplayName}"; this.Events.AddRange(this.Model.Events .Where(e => e.Category != EventCategory.Lifecycle || this.includeLifecycleEvents) .Where(e => e.Category != EventCategory.System || this.includeSystemEvents) .Where(e => e.Category != EventCategory.Access || this.includeAccessEvents)); } } //--------------------------------------------------------------------- public class Timeframe { public TimeSpan Duration { get; } public string Description { get; } public Timeframe(TimeSpan duration, string description) { this.Duration = duration; this.Description = description; } public override string ToString() { return this.Description; } } } }