sources/Google.Solutions.IapDesktop.Extensions.Management/ToolWindows/InstanceProperties/InstancePropertiesInspectorModel.cs (345 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.Apis.Compute.v1.Data;
using Google.Solutions.Apis;
using Google.Solutions.Apis.Compute;
using Google.Solutions.Apis.Locator;
using Google.Solutions.Common.Diagnostics;
using Google.Solutions.Common.Linq;
using Google.Solutions.Common.Util;
using Google.Solutions.IapDesktop.Application;
using Google.Solutions.IapDesktop.Core.ObjectModel;
using Google.Solutions.IapDesktop.Extensions.Management.GuestOs.Inventory;
using Google.Solutions.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Google.Solutions.IapDesktop.Extensions.Management.ToolWindows.InstanceProperties
{
[Service]
public class InstancePropertiesInspectorModel
{
private static class Categories
{
public const string Instance = "Basic information";
public const string Security = "Security";
public const string Network = "Networking";
public const string Scheduling = "Scheduling";
public const string Os = "Operating system";
public const string GuestAgentConfiguration = "Guest agent configuration";
public const string InstanceConfiguration = "Configuration";
public const string SshConfiguration = "SSH configuration";
}
private readonly Project projectDetails;
private readonly Instance instanceDetails;
private readonly GuestOsInfo? guestOsInfo;
private FeatureFlag GetMetadataFeatureFlag(string key, bool trueMeansEnabled)
{
var effectiveValue = this.instanceDetails.GetFlag(this.projectDetails, key);
return effectiveValue == trueMeansEnabled
? FeatureFlag.Enabled
: FeatureFlag.Disabled;
}
internal InstancePropertiesInspectorModel(
InstanceLocator instance,
Project projectDetails,
Instance instanceDetails,
GuestOsInfo? guestOsInfo)
{
this.projectDetails = projectDetails.ExpectNotNull(nameof(projectDetails));
this.instanceDetails = instanceDetails.ExpectNotNull(nameof(instanceDetails));
this.guestOsInfo = guestOsInfo;
//
// Basic information
//
this.InstanceName = this.instanceDetails.Name;
this.InstanceId = this.instanceDetails.Id ?? 0;
this.Status = this.instanceDetails.Status;
this.Hostname = this.instanceDetails.Hostname;
this.MachineType = this.instanceDetails.MachineType != null
? MachineTypeLocator.Parse(this.instanceDetails.MachineType).Name
: null;
this.Licenses = this.instanceDetails.Disks
.EnsureNotNull()
.Where(d => d.Licenses != null && d.Licenses.Any())
.SelectMany(d => d.Licenses)
.Select(l => LicenseLocator.Parse(l).Name)
.ToList();
this.CpuPlatform = this.instanceDetails.CpuPlatform;
this.Labels = this.instanceDetails.Labels;
//
// Security.
//
var serviceAccount = this.instanceDetails.ServiceAccounts?.FirstOrDefault();
this.ServiceAccount = serviceAccount?.Email;
this.ServiceAccountScopes = serviceAccount?.Scopes ?? Array.Empty<string>();
this.VtpmEnabled = this.instanceDetails.ShieldedInstanceConfig?.EnableVtpm == true
? FeatureFlag.Enabled
: FeatureFlag.Disabled;
this.SecureBootEnabled = this.instanceDetails.ShieldedInstanceConfig?.EnableSecureBoot == true
? FeatureFlag.Enabled
: FeatureFlag.Disabled;
this.IntegrityMonitoringEnabled = this.instanceDetails.ShieldedInstanceConfig?.EnableIntegrityMonitoring == true
? FeatureFlag.Enabled
: FeatureFlag.Disabled;
//
// Network.
//
this.Tags = this.instanceDetails.Tags?.Items ?? Array.Empty<string>();
this.InternalIp = this.instanceDetails.PrimaryInternalAddress()?.ToString();
this.ExternalIp = this.instanceDetails.PublicAddress()?.ToString();
this.InternalZonalDnsName = new InternalDnsName.ZonalName(instance).Name;
//
// Scheduling.
//
this.IsSoleTenant = this.instanceDetails.Scheduling?.NodeAffinities != null &&
this.instanceDetails.Scheduling.NodeAffinities.Any();
this.IsPreemptible = this.instanceDetails.Scheduling?.Preemptible == true;
//
// OS Inventory data.
//
this.IsOsInventoryInformationPopulated = this.guestOsInfo != null;
this.Architecture = this.guestOsInfo?.Architecture;
this.KernelVersion = this.guestOsInfo?.KernelVersion;
this.OperatingSystemFullName = this.guestOsInfo?.OperatingSystemFullName;
this.OperatingSystemVersion = this.guestOsInfo?.OperatingSystemVersion?.ToString();
//
// Guest agent configuration.
//
this.OsInventory = GetMetadataFeatureFlag("enable-os-inventory", true);
this.Diagnostics = GetMetadataFeatureFlag("enable-diagnostics", true);
this.OsLogin = GetMetadataFeatureFlag("enable-oslogin", true);
this.OsLogin2FA = GetMetadataFeatureFlag("enable-oslogin-2fa", true);
this.OsLoginWithSecurityKey = GetMetadataFeatureFlag("enable-oslogin-sk", true);
this.BlockProjectSshKeys = GetMetadataFeatureFlag("block-project-ssh-keys", true);
//
// Instance configuration.
//
this.SerialPortAccess = GetMetadataFeatureFlag("serial-port-enable", true);
this.GuestAttributes = GetMetadataFeatureFlag("enable-guest-attributes", true);
//
// Metadata, where instance metadata overrides project metadata.
//
var metadata = (this.projectDetails.CommonInstanceMetadata?.Items)
.EnsureNotNull()
.ToDictionary(i => i.Key, i => i.Value);
(this.instanceDetails.Metadata?.Items)
.EnsureNotNull()
.ToList()
.ForEach(i => metadata[i.Key] = i.Value);
this.Metadata = metadata;
}
//---------------------------------------------------------------------
// Basic information.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Name")]
[Description("Name of the VM instance")]
public string InstanceName { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("ID")]
[Description("The unique ID of the VM instance")]
public ulong InstanceId { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Status")]
[Description("The current status of the VM, see " +
"https://cloud.google.com/compute/docs/instances/instance-life-cycle")]
public string Status { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Hostname")]
[Description("The custom hostname, see " +
"https://cloud.google.com/compute/docs/instances/custom-hostname-vm")]
public string Hostname { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Machine type")]
[Description("The type and size of VM, see " +
"https://cloud.google.com/compute/docs/machine-types")]
public string? MachineType { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("CPU platform")]
[Description("CPU platform, see " +
"https://cloud.google.com/compute/docs/cpu-platforms")]
public string CpuPlatform { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Licenses")]
[Description("The licenses applied to the VM, see " +
"https://cloud.google.com/sdk/gcloud/reference/compute/images/import#--os")]
[TypeConverter(typeof(ExpandableCollectionConverter))]
public ICollection<string> Licenses { get; }
[Browsable(true)]
[Category(Categories.Instance)]
[DisplayName("Labels")]
[Description("Labels, see " +
"https://cloud.google.com/compute/docs/labeling-resources")]
[TypeConverter(typeof(ExpandableCollectionConverter))]
public IDictionary<string, string> Labels { get; }
//---------------------------------------------------------------------
// Security.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.Security)]
[DisplayName("Service account")]
[Description("The service account that is attached to this instance")]
public string? ServiceAccount { get; }
[Browsable(true)]
[Category(Categories.Security)]
[DisplayName("Service account scopes")]
[Description("OAuth scopes for which this VM can obtain credentials")]
[TypeConverter(typeof(ExpandableCollectionConverter))]
public ICollection<string> ServiceAccountScopes { get; }
[Browsable(true)]
[Category(Categories.Security)]
[DisplayName("vTPM")]
[Description("Indicates whether this VM has a virtual TPM device")]
public FeatureFlag VtpmEnabled { get; }
[Browsable(true)]
[Category(Categories.Security)]
[DisplayName("Secure boot")]
[Description("Indicates whether this VM uses secure boot")]
public FeatureFlag SecureBootEnabled { get; }
[Browsable(true)]
[Category(Categories.Security)]
[DisplayName("Integrity monitoring")]
[Description("Indicates whether this uses integrity monitoring")]
public FeatureFlag IntegrityMonitoringEnabled { get; }
//---------------------------------------------------------------------
// Network.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.Network)]
[DisplayName("Network tags")]
[Description("Network tags, see " +
"https://cloud.google.com/vpc/docs/add-remove-network-tags")]
[TypeConverter(typeof(ExpandableCollectionConverter))]
public ICollection<string> Tags { get; }
[Browsable(true)]
[Category(Categories.Network)]
[DisplayName("IP address (internal)")]
[Description("The VM's primary internal IP address, see " +
"https://cloud.google.com/compute/docs/ip-addresses#networkaddresses")]
public string? InternalIp { get; }
[Browsable(true)]
[Category(Categories.Network)]
[DisplayName("IP address (external)")]
[Description("The VM's external IP address, see " +
"https://cloud.google.com/compute/docs/ip-addresses#externaladdresses")]
public string? ExternalIp { get; }
[Browsable(true)]
[Category(Categories.Network)]
[DisplayName("Internal DNS name")]
[Description("Internal zonal DNS name, see " +
"https://cloud.google.com/compute/docs/internal-dns#about_internal_dns")]
public string InternalZonalDnsName { get; }
//---------------------------------------------------------------------
// Scheduling.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.Scheduling)]
[DisplayName("Sole tenant VM")]
[Description("Indicates whether this VM is scheduled to run on a sole-tenant node, see " +
"https://cloud.google.com/compute/docs/nodes/sole-tenant-nodes")]
public bool IsSoleTenant { get; }
[Browsable(true)]
[Category(Categories.Scheduling)]
[DisplayName("Preemptible VM")]
[Description("Indicates whether this VM is preemptible, see " +
"https://cloud.google.com/compute/docs/instances/preemptible")]
public bool IsPreemptible { get; }
//---------------------------------------------------------------------
// OS Inventory data.
//---------------------------------------------------------------------
[Browsable(false)]
public bool IsOsInventoryInformationPopulated { get; }
[Browsable(true)]
[Category(Categories.Os)]
[DisplayName("Architecture")]
[Description("The VM's CPU architecture")]
public string? Architecture { get; }
[Browsable(true)]
[Category(Categories.Os)]
[DisplayName("Kernel")]
[Description("The guest operating system's kernel version")]
public string? KernelVersion { get; }
[Browsable(true)]
[Category(Categories.Os)]
[DisplayName("Name")]
[Description("The name of the guest operating system")]
public string? OperatingSystemFullName { get; }
[Browsable(true)]
[Category(Categories.Os)]
[DisplayName("Version")]
[Description("The version of the guest operating system")]
public string? OperatingSystemVersion { get; }
//---------------------------------------------------------------------
// Guest agent configuration.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.GuestAgentConfiguration)]
[DisplayName("OS Inventory")]
[Description("Indicates whether OS inventory management is enabled, " +
"see https://cloud.google.com/compute/docs/instances/" +
"view-os-details#enable-guest-attributes")]
public FeatureFlag OsInventory { get; }
[Browsable(true)]
[Category(Categories.GuestAgentConfiguration)]
[DisplayName("Diagnostics")]
[Description("Indicates whether the collection of diagnostic information is enabled, " +
"see https://cloud.google.com/compute/docs/instances/" +
"collecting-diagnostic-information#collecting_diagnostic_information_from_a_vm")]
public FeatureFlag Diagnostics { get; }
//---------------------------------------------------------------------
// SSH configuration.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.SshConfiguration)]
[DisplayName("OS Login")]
[Description("Indicates whether OS Login is enabled, " +
"see https://cloud.google.com/compute/docs/instances/managing-instance-access.")]
public FeatureFlag OsLogin { get; }
[Browsable(true)]
[Category(Categories.SshConfiguration)]
[Description("Indicates whether the instance requires multi-factor authentication for SSH " +
"see https://cloud.google.com/compute/docs/oslogin/setup-two-factor-authentication.")]
[DisplayName("OS Login 2FA")]
public FeatureFlag OsLogin2FA { get; }
[Browsable(true)]
[Category(Categories.SshConfiguration)]
[Description("Indicates whether the instance requires a security key for SSH, " +
"see https://cloud.google.com/compute/docs/oslogin/security-keys.")]
[DisplayName("OS Login Security Key")]
public FeatureFlag OsLoginWithSecurityKey { get; }
[Browsable(true)]
[Category(Categories.SshConfiguration)]
[DisplayName("Block project-wide SSH keys")]
[Description("Indicates whether project-side SSH keys are disabled for this VM, " +
"see https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys#block-project-keys.")]
public FeatureFlag BlockProjectSshKeys { get; }
//---------------------------------------------------------------------
// Instance configuration.
//---------------------------------------------------------------------
[Browsable(true)]
[Category(Categories.InstanceConfiguration)]
[DisplayName("Serial port access")]
[Description("Indicates whether the special administrative console can be used, " +
"see https://cloud.google.com/compute/docs/instances/" +
"interacting-with-serial-console#enable_project_access")]
public FeatureFlag SerialPortAccess { get; }
[Browsable(true)]
[Category(Categories.InstanceConfiguration)]
[DisplayName("Guest attributes")]
[Description("Indicates whether guest attributes are enabled, " +
"see https://cloud.google.com/compute/docs/storing-retrieving-metadata#enable_attributes")]
public FeatureFlag GuestAttributes { get; }
[Browsable(true)]
[Category(Categories.InstanceConfiguration)]
[DisplayName("Metadata")]
[TypeConverter(typeof(ExpandableCollectionConverter))]
[Description("Merge result of project and instance metadata, " +
"see https://cloud.google.com/compute/docs/metadata/overview")]
public IDictionary<string, string> Metadata { get; }
//---------------------------------------------------------------------
// Loading.
//---------------------------------------------------------------------
public override string ToString() => this.InstanceName;
public static async Task<InstancePropertiesInspectorModel> LoadAsync(
InstanceLocator instanceLocator,
IComputeEngineClient computeClient,
IGuestOsInventory packageInventory,
CancellationToken token)
{
var instance = await computeClient
.GetInstanceAsync(
instanceLocator,
token)
.ConfigureAwait(false);
var project = await computeClient
.GetProjectAsync(
instanceLocator.Project,
token)
.ConfigureAwait(false);
//
// Reading OS inventory data can fail because of a
// `compute.disableGuestAttributesAccess` constraint.
//
GuestOsInfo? osInfo;
try
{
osInfo = await packageInventory.GetInstanceInventoryAsync(
instanceLocator,
token)
.ConfigureAwait(false);
}
catch (Exception e) when (e.Unwrap() is GoogleApiException apiEx &&
apiEx.IsConstraintViolation())
{
ApplicationTraceSource.Log.TraceWarning(
"Failed to load OS inventory data: {0}", e);
// Proceed with empty data.
osInfo = null;
}
return new InstancePropertiesInspectorModel(
instanceLocator,
project,
instance,
osInfo);
}
}
public enum FeatureFlag
{
Enabled,
Disabled
}
}