From 7cbf9d68d825f614755694a5ea1da97269f3b5e1 Mon Sep 17 00:00:00 2001 From: BasiliusCarver Date: Fri, 29 Apr 2022 16:04:45 +1200 Subject: [PATCH] Add details to tree nodes to make loadbalancers more visible and make powershell pktmon runner less garbage --- Data/HnsDatasource.cs | 168 ++++++++++++++++++ Extensions/JsonElementExtensions.cs | 193 +++++++++++++-------- Extensions/ProgressBarExtensions.cs | 49 ++++++ Extensions/TreeNodeCollectionExtensions.cs | 12 +- HnsExplorer.csproj | 15 ++ HostContainerServices/HcsAccess.cs | 12 +- HostContainerServices/HnsAccess.cs | 128 +++++++++++--- HostContainerServices/HostNetworkAccess.cs | 170 ++++++++++++++++++ Program.cs | 11 +- Properties/Resources.Designer.cs | 73 ++++++++ Properties/Resources.resx | 124 +++++++++++++ Resources/HnsExplorerIconLarge.png | Bin 0 -> 6629 bytes SplashForm.Designer.cs | 111 ++++++++++++ SplashForm.cs | 39 +++++ SplashForm.resx | 63 +++++++ SummaryForm.Designer.cs | 5 +- SummaryForm.cs | 129 ++++++-------- 17 files changed, 1117 insertions(+), 185 deletions(-) create mode 100644 Data/HnsDatasource.cs create mode 100644 Extensions/ProgressBarExtensions.cs create mode 100644 HostContainerServices/HostNetworkAccess.cs create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.resx create mode 100644 Resources/HnsExplorerIconLarge.png create mode 100644 SplashForm.Designer.cs create mode 100644 SplashForm.cs create mode 100644 SplashForm.resx diff --git a/Data/HnsDatasource.cs b/Data/HnsDatasource.cs new file mode 100644 index 0000000..ab4826b --- /dev/null +++ b/Data/HnsDatasource.cs @@ -0,0 +1,168 @@ +using System.Text.Json; +using HnsExplorer.HostContainerServices; +using HnsExplorer.Extensions; + +namespace HnsExplorer.Data +{ + public class HnsDatasource + { + + private readonly HnsAccess HnsAccess = new(); + private readonly HcsAccess HcsAccess = new(); + + public int NumberOfStepsLoaded { get; private set; } + public int NumberOfStepsTotal { get; private set; } + + public string SummaryOutput { get; private set; } + public string LoadingState { get; private set; } + public string ExportDataSnapshot { get; private set; } + + public TreeNode RoutesNode { get; private set; } + public TreeNode ActivitiesNode { get; private set; } + public TreeNode OrphansNode { get; private set; } + public TreeNode NamespacesNode { get; private set; } + + public HnsDatasource() + { + NumberOfStepsLoaded = 0; + NumberOfStepsTotal = 20; + LoadingState = "Initialised"; + SummaryOutput = "No data"; + ExportDataSnapshot = "{}"; + RoutesNode = new TreeNode(); + ActivitiesNode = new TreeNode(); + OrphansNode = new TreeNode(); + NamespacesNode = new TreeNode(); + } + + private void UpdateLoadingState(string message) + { + LoadingState = message; + if(NumberOfStepsLoaded < NumberOfStepsTotal) + { + NumberOfStepsLoaded++; + } + } + public void Reset() + { + NumberOfStepsLoaded = 0; + LoadingState = "Reload required"; + SummaryOutput = "No data"; + ExportDataSnapshot = "{}"; + RoutesNode = new TreeNode(); + ActivitiesNode = new TreeNode(); + OrphansNode = new TreeNode(); + NamespacesNode = new TreeNode(); + } + + public void Load() + { + UpdateLoadingState("Loading activities..."); + var activitiesData = HnsAccess.GetActivities(); + UpdateLoadingState("Loading namespaces..."); + var namespaceData = HnsAccess.GetNamespaces(); + UpdateLoadingState("Loading networks..."); + var networkData = HnsAccess.GetNetworks(); + UpdateLoadingState("Loading policies..."); + var policyData = HnsAccess.GetPolicyLists(); + UpdateLoadingState("Loading endpoints..."); + var endpointData = HnsAccess.GetEndpoints(); + UpdateLoadingState("Loading compute..."); + var computeData = HcsAccess.GetComputeSystems(); + UpdateLoadingState("Loading routes..."); + var routeData = HostNetworkAccess.GetIpForwardTable(); + + LoadingState = "Building summary..."; + SummaryOutput = $"Activities: {activitiesData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Namespaces: {namespaceData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Networks: {networkData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Network policies: {policyData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Network endpoints: {endpointData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Compute systems: {computeData.Count()}{Environment.NewLine}"; + SummaryOutput += $"Host routes: {routeData.Count()}{Environment.NewLine}"; + + UpdateLoadingState("Building routes output..."); + var hostRouteList = $"{"Destination",16} {"Netmask",16} {"Gateway",16} {"Interface",16} {"IfIndex",8} {"Type",8} {"Protocol",14} {"Metric",7}{Environment.NewLine}"; + hostRouteList += $"------------------------------------------------------------------------------------------------------------{Environment.NewLine}"; + foreach (var row in routeData) + { + hostRouteList += $"{row.ForwardDest,16} {row.ForwardMask,16} {row.ForwardNextHop,16} {row.IfIpAddress,16} {row.IfIndex,8} {row.RouteType,8} {row.ForwardProto,14} {row.PrimaryMetric,7}{row.Error}{Environment.NewLine}"; + } + RoutesNode = new TreeNode + { + Text = "HostRoutes", + Tag = hostRouteList + }; + + ActivitiesNode = new TreeNode + { + Text = "Activities", + Tag = SummaryOutput + }; + + UpdateLoadingState("Building activities tree..."); + var orphanedActivities = ActivitiesNode.Nodes.InsertNestedChildren(activitiesData, "ID", "parentId", "Activity", "Allocators.Tag"); + + UpdateLoadingState("Inserting network children..."); + var orphanedNetworks = ActivitiesNode.Nodes.InsertNestedChildren(networkData, "ID", "Resources.ID", "Network", "Name,' ',ManagementIP"); + + UpdateLoadingState("Inserting endpoint children..."); + var orphanedEndpoints = ActivitiesNode.Nodes.InsertNestedChildren(endpointData, "ID", "Resources.ID", "Endpoint", "Name,' ',IPAddress"); + + UpdateLoadingState("Inserting policy children..."); + var orphanedPolicies = ActivitiesNode.Nodes.InsertNestedChildren(policyData, "ID", "Resources.ID", "Policy", "Policies.Type,' ',Policies.SourceVIP,' ->',Policies.VIPs,' ',Policies.ExternalPort,':',Policies.InternalPort"); + + UpdateLoadingState("Getting endpointstats..."); + var endpointIds = new List(); + foreach (var item in endpointData) + { + var id = item.GetJsonDataAsString("ID"); + endpointIds.Add(id); + } + var endpointStatsData = HnsAccess.GetEndpointStats(endpointIds); + UpdateLoadingState("Inserting endpointstats children..."); + ActivitiesNode.Nodes.InsertNestedChildren(endpointStatsData, "InstanceId", "EndpointId", "Endpoint Stats", "Name"); + + UpdateLoadingState("Inserting virtualmachine children..."); + var orphanedCompute = ActivitiesNode.Nodes.InsertChildrenWithMatchingParentReference(computeData, endpointData, "Id", "ID", "VirtualMachine", "Virtual Machine", "Owner"); + UpdateLoadingState("Inserting container children..."); + orphanedCompute = ActivitiesNode.Nodes.InsertChildrenWithMatchingParentReference(orphanedCompute, endpointData, "Id", "ID", "SharedContainers", "Container", "Owner"); + + UpdateLoadingState("Building orphan tree..."); + OrphansNode = new TreeNode("Orphaned Data"); + OrphansNode.Nodes.InsertChildren(orphanedActivities, "ID", "Activities", "Allocators.Tag"); + OrphansNode.Nodes.InsertChildren(orphanedNetworks, "ID", "Network", "Name"); + OrphansNode.Nodes.InsertChildren(orphanedEndpoints, "ID", "Endpoint", "Name"); + OrphansNode.Nodes.InsertChildren(orphanedPolicies, "ID", "Endpoint", "Name"); + OrphansNode.Nodes.InsertChildren(orphanedCompute, "ID", "Compute", "Name"); + + UpdateLoadingState("Building namespaces tree..."); + NamespacesNode = new TreeNode + { + Text = "Namespaces", + Tag = $"{namespaceData.Count()} namespaces" + }; + NamespacesNode.Nodes.InsertChildren(namespaceData, "ID", "Namespace", "CompartmentId"); + + UpdateLoadingState("Building data snapshot..."); + var allData = new Dictionary> + { + { "Namespaces", namespaceData }, + { "Network", networkData }, + { "Policies", policyData }, + { "Endpoints", endpointData }, + { "Compute", computeData }, + { "Activities", activitiesData }, + { "EndpointStats", endpointStatsData } + }; + var routeString = JsonSerializer.Serialize(routeData); + var routeJsonElement = JsonSerializer.Deserialize>(routeString); + if(routeJsonElement is not null) + { + allData.Add("Routes", routeJsonElement); + } + ExportDataSnapshot = JsonSerializer.Serialize(allData); + UpdateLoadingState("Done"); + } + } +} diff --git a/Extensions/JsonElementExtensions.cs b/Extensions/JsonElementExtensions.cs index e6396e4..0311097 100644 --- a/Extensions/JsonElementExtensions.cs +++ b/Extensions/JsonElementExtensions.cs @@ -6,71 +6,115 @@ public static class JsonElementExtensions { public static string GetJsonDataAsString(this JsonElement element, string properties) { - var thisPropertyName = properties; - string remainingProperties = string.Empty; - if (properties.Contains('.')) + try { - thisPropertyName = properties.Split('.').First(); - remainingProperties = string.Join('.', properties.Split('.').Skip(1)); - } - if (element.ValueKind == JsonValueKind.Array) - { - var result = new List(); - var arrayItems = element.EnumerateArray(); - foreach (var arrayItem in arrayItems) - { - result.Add(arrayItem.GetJsonDataAsString(properties)); - } - return string.Join(", ", result); - } - else - { - if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) + var multiProperty = properties.Split(','); + var resultText = new List(); + foreach (var property in multiProperty) { - if (remainingProperties.Equals(string.Empty)) + if(property.StartsWith("'") && property.EndsWith("'")) { - if(innerElement.ValueKind == JsonValueKind.Number) - { - return innerElement.GetInt32().ToString() ?? "No data found"; - } - else + resultText.Add(property[1..^1]); + continue; + } + var thisPropertyName = property; + string remainingProperties = string.Empty; + if (property.Contains('.')) + { + thisPropertyName = property.Split('.').First(); + remainingProperties = string.Join('.', property.Split('.').Skip(1)); + } + + if (element.ValueKind == JsonValueKind.Array) + { + var result = new List(); + var arrayItems = element.EnumerateArray(); + foreach (var arrayItem in arrayItems) { - return innerElement.GetString() ?? "No data found"; + result.Add(arrayItem.GetJsonDataAsString(property)); } + resultText.Add(string.Join(", ", result.Where(s => !string.IsNullOrEmpty(s)))); } else { - return GetJsonDataAsString(innerElement, remainingProperties); + if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) + { + if (remainingProperties.Equals(string.Empty)) + { + if (innerElement.ValueKind == JsonValueKind.Array) + { + var elements = innerElement.EnumerateArray(); + foreach (var elementItem in elements) + { + if (elementItem.ValueKind == JsonValueKind.Number) + { + resultText.Add(elementItem.GetInt32().ToString() ?? "No data found"); + } + else + { + resultText.Add(elementItem.GetString() ?? "No data found"); + } + } + } + else + { + if (innerElement.ValueKind == JsonValueKind.Number) + { + resultText.Add(innerElement.GetInt32().ToString() ?? "No data found"); + } + else + { + resultText.Add(innerElement.GetString() ?? "No data found"); + } + } + } + else + { + resultText.Add(GetJsonDataAsString(innerElement, remainingProperties)); + } + } + else + { + // no data + } } } - else - { - return $"No data at [{thisPropertyName}]"; - } + return string.Join("", resultText); + } + catch (Exception ex) + { + return $"Failed to get property [{properties}] detail: {ex.Message}"; } } public static bool HasJsonData(this JsonElement element, string properties) { - var thisPropertyName = properties; - string remainingProperties = string.Empty; - if (properties.Contains('.')) - { - thisPropertyName = properties.Split('.').First(); - remainingProperties = string.Join('.', properties.Split('.').Skip(1)); - } - if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) + try { - if (remainingProperties.Equals(string.Empty)) + var thisPropertyName = properties; + string remainingProperties = string.Empty; + if (properties.Contains('.')) + { + thisPropertyName = properties.Split('.').First(); + remainingProperties = string.Join('.', properties.Split('.').Skip(1)); + } + if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) { - return true; + if (remainingProperties.Equals(string.Empty)) + { + return true; + } + else + { + return HasJsonData(innerElement, remainingProperties); + } } else { - return HasJsonData(innerElement, remainingProperties); + return false; } } - else + catch { return false; } @@ -91,46 +135,53 @@ public static bool HasJsonData(this JsonElement element, string properties) public static JsonElement? FirstElementMatchingQuery(this JsonElement element, string properties, string expectedValue) { - var thisPropertyName = properties; - string remainingProperties = string.Empty; - if (properties.Contains('.')) - { - thisPropertyName = properties.Split('.').First(); - remainingProperties = string.Join('.', properties.Split('.').Skip(1)); - } - if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) + try { - if (remainingProperties.Equals(string.Empty)) + var thisPropertyName = properties; + string remainingProperties = string.Empty; + if (properties.Contains('.')) { - string result; - if (innerElement.ValueKind == JsonValueKind.Number) - { - result = innerElement.GetInt32().ToString(); - } - else if (innerElement.ValueKind == JsonValueKind.Array) - { - var arrayValues = innerElement.EnumerateArray(); - result = string.Join(", ", arrayValues); - } - else - { - result = innerElement.GetString() ?? string.Empty; - } - if(result.Contains(expectedValue)) + thisPropertyName = properties.Split('.').First(); + remainingProperties = string.Join('.', properties.Split('.').Skip(1)); + } + if (element.TryGetProperty(thisPropertyName, out JsonElement innerElement)) + { + if (remainingProperties.Equals(string.Empty)) { - return element; + string result; + if (innerElement.ValueKind == JsonValueKind.Number) + { + result = innerElement.GetInt32().ToString(); + } + else if (innerElement.ValueKind == JsonValueKind.Array) + { + var arrayValues = innerElement.EnumerateArray(); + result = string.Join(", ", arrayValues); + } + else + { + result = innerElement.GetString() ?? string.Empty; + } + if (result.Contains(expectedValue)) + { + return element; + } + else + { + return null; + } } else { - return null; + return element.FirstElementMatchingQuery(remainingProperties, expectedValue); } } else { - return element.FirstElementMatchingQuery(remainingProperties, expectedValue); + return null; } } - else + catch { return null; } diff --git a/Extensions/ProgressBarExtensions.cs b/Extensions/ProgressBarExtensions.cs new file mode 100644 index 0000000..acf711f --- /dev/null +++ b/Extensions/ProgressBarExtensions.cs @@ -0,0 +1,49 @@ +using System.Drawing.Drawing2D; + +namespace HnsExplorer.Extensions +{ + public class ProgressBarExtended : ProgressBar + { + private SolidBrush? ForegroundBrush; + private SolidBrush? BackgroundBrush; + private Rectangle LoadingBarOuter; + private Rectangle LoadingBarInner; + public ProgressBarExtended() + { + this.SetStyle(ControlStyles.UserPaint, true); + } + + protected override void OnPaint(PaintEventArgs e) + { + if (ForegroundBrush == null) + { + ForegroundBrush = new SolidBrush(ForeColor); + } + if (BackgroundBrush == null) + { + BackgroundBrush = new SolidBrush(BackColor); + } + if(LoadingBarOuter.IsEmpty) + { + LoadingBarOuter = new Rectangle(0, 0, Width, Height); + DrawHorizontalBar(e.Graphics, BackgroundBrush, LoadingBarOuter); + } + + double scaleFactor = (double)(Value - Minimum) / (Maximum - Minimum); + int currentProgress = (int)((LoadingBarOuter.Width * scaleFactor) - 2); + + if (LoadingBarInner.IsEmpty) + { + LoadingBarInner = new Rectangle(1, 1, currentProgress, LoadingBarOuter.Height - 2); + } + + LoadingBarInner.Width = currentProgress; + DrawHorizontalBar(e.Graphics, ForegroundBrush, LoadingBarInner); + } + + private void DrawHorizontalBar(Graphics g, Brush b, Rectangle bounds) + { + g.FillRectangle(b, bounds); + } + } +} diff --git a/Extensions/TreeNodeCollectionExtensions.cs b/Extensions/TreeNodeCollectionExtensions.cs index cbc44d4..da73d38 100644 --- a/Extensions/TreeNodeCollectionExtensions.cs +++ b/Extensions/TreeNodeCollectionExtensions.cs @@ -16,9 +16,9 @@ public static void InsertChildren(this TreeNodeCollection element, var id = item.GetJsonDataAsString(identifier); var nodeText = nodeTextPrefix; var suffix = item.GetJsonDataAsString(nodeTextSuffix); - if (!suffix.Contains("No data")) + if (!string.IsNullOrWhiteSpace(suffix)) { - nodeText += $" [{suffix}]"; + nodeText += $" [{suffix.Trim()}]"; } element.Add(new TreeNode() { @@ -43,9 +43,9 @@ public static IEnumerable InsertNestedChildren(this TreeNodeCollect var id = item.GetJsonDataAsString(identifier); var nodeText = nodeTextPrefix; var suffix = item.GetJsonDataAsString(nodeTextSuffix); - if (!suffix.Contains("No data")) + if (!string.IsNullOrWhiteSpace(suffix)) { - nodeText += $" [{suffix}]"; + nodeText += $" [{suffix.Trim()}]"; } var hasParent = item.HasJsonData(parentIdentifier); @@ -111,9 +111,9 @@ public static IEnumerable InsertChildrenWithMatchingParentReference var id = item.GetJsonDataAsString(identifier); var nodeText = nodeTextPrefix; var suffix = item.GetJsonDataAsString(nodeTextSuffix); - if(!suffix.Contains("No data")) + if (!string.IsNullOrWhiteSpace(suffix)) { - nodeText += $" [{suffix}]"; + nodeText += $" [{suffix.Trim()}]"; } var parentData = elementsToSearchForParentId.FirstElementMatchingQuery(parentFieldReferencingIdentifier, id); diff --git a/HnsExplorer.csproj b/HnsExplorer.csproj index 5512c49..20331e5 100644 --- a/HnsExplorer.csproj +++ b/HnsExplorer.csproj @@ -23,4 +23,19 @@ + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + \ No newline at end of file diff --git a/HostContainerServices/HcsAccess.cs b/HostContainerServices/HcsAccess.cs index b5478e9..8de4faa 100644 --- a/HostContainerServices/HcsAccess.cs +++ b/HostContainerServices/HcsAccess.cs @@ -14,8 +14,16 @@ public HcsAccess() public IEnumerable GetComputeSystems() { - _hcs.EnumerateComputeSystems(null, out string computeSystems); - return JsonSerializer.Deserialize>(computeSystems) ?? new List(); + try + { + _hcs.EnumerateComputeSystems(null, out string computeSystems); + return JsonSerializer.Deserialize>(computeSystems) ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } } } } diff --git a/HostContainerServices/HnsAccess.cs b/HostContainerServices/HnsAccess.cs index 2614988..da9782c 100644 --- a/HostContainerServices/HnsAccess.cs +++ b/HostContainerServices/HnsAccess.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using System.Runtime.InteropServices; using Microsoft.Windows.ComputeVirtualization; namespace HnsExplorer.HostContainerServices @@ -12,6 +13,15 @@ public HnsAccess() _hns = HnsFactory.GetHns(); } + [DllImport("computenetwork.dll", ExactSpelling = true)] + private static extern void HcnEnumerateLoadBalancers(string filter, [MarshalAs(UnmanagedType.LPWStr)] out string loadBalancersString, [MarshalAs(UnmanagedType.LPWStr)] out string errorRecord); + [DllImport("computenetwork.dll", ExactSpelling = true)] + private static extern void HcnOpenLoadBalancer(Guid lbguid, out IntPtr loadBalancerHandle, [MarshalAs(UnmanagedType.LPWStr)] out string errorRecord); + [DllImport("computenetwork.dll", ExactSpelling = true)] + private static extern void HcnCloseLoadBalancer(IntPtr loadBalancerHandle); + [DllImport("computenetwork.dll", ExactSpelling = true)] + private static extern void HcnQueryLoadBalancerProperties(IntPtr handle, string query, [MarshalAs(UnmanagedType.LPWStr)] out string properties, [MarshalAs(UnmanagedType.LPWStr)] out string errorRecord); + private class HnsResponse { public bool Success { get; set; } public JsonElement Output { get; set; } @@ -25,52 +35,126 @@ private class HnsResponseCollection public IEnumerable GetActivities() { - _hns.Call("GET", "/activities/", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - return hnsResponse?.Output ?? new List(); + try + { + _hns.Call("GET", "/activities/", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + return hnsResponse?.Output ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } } public IEnumerable GetNamespaces() { - _hns.Call("GET", "/namespaces/", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - return hnsResponse?.Output ?? new List(); + try + { + _hns.Call("GET", "/namespaces/", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + return hnsResponse?.Output ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } } public IEnumerable GetNetworks() { - _hns.Call("GET", "/networks/", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - return hnsResponse?.Output ?? new List(); + try + { + _hns.Call("GET", "/networks/", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + return hnsResponse?.Output ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } } public IEnumerable GetPolicyLists() { - _hns.Call("GET", "/policylists/", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - return hnsResponse?.Output ?? new List(); + try + { + _hns.Call("GET", "/policylists/", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + return hnsResponse?.Output ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } + } + + public IEnumerable GetLoadBalancers() + { + try + { + HcnEnumerateLoadBalancers("", out string loadBalancers, out string errorRecord); + var hnsResponse = JsonSerializer.Deserialize>(loadBalancers); + var loadBalancerProperties = new List(); + if(hnsResponse is not null) + { + foreach (var loadbalancer in hnsResponse) + { + HcnOpenLoadBalancer(new Guid(loadbalancer), out IntPtr handle, out string errorRecord1); + HcnQueryLoadBalancerProperties(handle, "", out string properties, out string errorRecord2); + HcnCloseLoadBalancer(handle); + loadBalancerProperties.Add(JsonSerializer.Deserialize(properties)); + } + } + return loadBalancerProperties; + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize($"Failed to get loadbalancers: {ex.Message}\n{ex.StackTrace}")); + return new List { error }; + } } public IEnumerable GetEndpoints() { - _hns.Call("GET", "/endpoints/", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - return hnsResponse?.Output ?? new List(); + try + { + _hns.Call("GET", "/endpoints/", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + return hnsResponse?.Output ?? new List(); + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; + } } public IEnumerable GetEndpointStats(IEnumerable endpointIds) { - var endpointStats = new List(); - foreach(var endpointId in endpointIds) + try { - _hns.Call("GET", $"/endpointstats/{endpointId}", "", out string response); - var hnsResponse = JsonSerializer.Deserialize(response); - if(hnsResponse?.Output != null) + var endpointStats = new List(); + foreach(var endpointId in endpointIds) { - endpointStats.Add(hnsResponse.Output); + _hns.Call("GET", $"/endpointstats/{endpointId}", "", out string response); + var hnsResponse = JsonSerializer.Deserialize(response); + if(hnsResponse?.Output != null) + { + endpointStats.Add(hnsResponse.Output); + } } + return endpointStats; + } + catch (Exception ex) + { + var error = JsonSerializer.Deserialize(JsonSerializer.Serialize(ex)); + return new List { error }; } - return endpointStats; } } } diff --git a/HostContainerServices/HostNetworkAccess.cs b/HostContainerServices/HostNetworkAccess.cs new file mode 100644 index 0000000..b0aa9a2 --- /dev/null +++ b/HostContainerServices/HostNetworkAccess.cs @@ -0,0 +1,170 @@ +using System.Net; +using System.Net.Sockets; +using System.Text.Json; +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; + +namespace HnsExplorer.HostContainerServices +{ + public static class HostNetworkAccess + { + public enum RouteType + { + OTHER = 1, + INVALID = 2, + DIRECT = 3, + INDIRECT = 4 + } + public enum ForwardProto + { + OTHER = 1, + LOCAL = 2, + NETMGMT = 3, + ICMP = 4, + EGP = 5, + GGP = 6, + HELLO = 7, + RIP = 8, + IS_IS = 9, + ES_IS = 10, + CISCO = 11, + BBN = 12, + OSPF = 13, + BGP = 14, + AUTOSTATIC = 10002, + STATIC = 10006, + STATIC_NON_DOD = 1000 + } + + [StructLayout(LayoutKind.Sequential)] + public struct MIB_IPFORWARDROW + { + public uint dwForwardDest; + public uint dwForwardMask; + public uint dwForwardPolicy; + public uint dwForwardNextHop; + public uint dwForwardIfIndex; + public uint dwForwardType; + public uint dwForwardProto; + public uint dwForwardAge; + public uint dwForwardNextHopAS; + public int dwForwardMetric1; + public int dwForwardMetric2; + public int dwForwardMetric3; + public int dwForwardMetric4; + public int dwForwardMetric5; + } + + [DllImport("iphlpapi.dll")] + [return: MarshalAs(UnmanagedType.U4)] + private static extern int GetIpForwardTable(IntPtr pIpForwardTable, out int pdwSize, bool bOrder); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used during json serialization to avoid socket errors when trying to serialize IPAddress")] + public class IpRoute + { + [JsonIgnore] + public IPAddress ForwardDest { get; set; } + [JsonIgnore] + public IPAddress ForwardMask { get; set; } + [JsonIgnore] + public IPAddress ForwardNextHop { get; set; } + [JsonIgnore] + public IPAddress IfIpAddress { get; set; } + [JsonPropertyName("ForwardDest")] + private string ForwardDestString => ForwardDest.ToString(); + [JsonPropertyName("ForwardDest")] + private string ForwardMaskString => ForwardMask.ToString(); + [JsonPropertyName("ForwardNextHop")] + private string ForwardNextHopString => ForwardNextHop.ToString(); + [JsonPropertyName("IfIpAddress")] + private string IfIpAddressString => IfIpAddress.ToString(); + public int IfIndex { get; set; } + public ForwardProto ForwardProto { get; set; } + public RouteType RouteType { get; set; } + public int PrimaryMetric { get; set; } + public string Error { get; set; } + + public IpRoute(uint forwardDest, uint forwardMask, uint forwardNextHop, int ifIndex, IPAddress ifIpAddress, uint routeType, uint forwardProto, int primaryMetric) + { + ForwardDest = new IPAddress(forwardDest); + ForwardMask = new IPAddress(forwardMask); + ForwardNextHop = new IPAddress(forwardNextHop); + ForwardProto = (ForwardProto)forwardProto; + IfIndex = ifIndex; + IfIpAddress = ifIpAddress; + RouteType = (RouteType)routeType; + PrimaryMetric = primaryMetric; + Error = string.Empty; + } + + public IpRoute(string error) + { + ForwardDest = IPAddress.None; + ForwardMask = IPAddress.None; + ForwardNextHop = IPAddress.None; + ForwardProto = (ForwardProto)1; + IfIndex = -1; + IfIpAddress = IPAddress.None; + RouteType = (RouteType)1; + PrimaryMetric = -1; + Error = error; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "Native call requires the out param, discard doesn't work")] + public static IEnumerable GetIpForwardTable() + { + int bufferSize = 1; + IntPtr data = Marshal.AllocHGlobal(bufferSize); + try + { + GetIpForwardTable(data, out bufferSize, true); + data = Marshal.AllocHGlobal(bufferSize); + var ret = GetIpForwardTable(data, out bufferSize, true); + if (ret != 0) + { + return new List(); + } + int length = Marshal.ReadInt32(data); + var rowSize = Marshal.SizeOf(); + + var routeTable = new List(); + var interfaces = NetworkInterface.GetAllNetworkInterfaces(); + + for (int i = 0; i < length; i++) + { + var row = Marshal.PtrToStructure(data + sizeof(int) + (rowSize * i)); + var index = (int)row.dwForwardIfIndex; + var iface = interfaces.Where(i => i.GetIPProperties().GetIPv4Properties()?.Index == index).First(); + var ifIpAddress = iface.GetIPProperties().UnicastAddresses + .Where(u => u.Address.AddressFamily is AddressFamily.InterNetwork) + .First()?.Address; + if (ifIpAddress is null) + { + ifIpAddress = new IPAddress(0); + } + routeTable.Add(new IpRoute( + row.dwForwardDest, + row.dwForwardMask, + row.dwForwardNextHop, + index, + ifIpAddress, + row.dwForwardType, + row.dwForwardType, + row.dwForwardMetric1)); + } + return routeTable; + } + catch (Exception ex) + { + var error = JsonSerializer.Serialize(ex); + return new List { new IpRoute(error) }; + } + finally + { + Marshal.FreeHGlobal(data); + } + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 0719323..81a4a8e 100644 --- a/Program.cs +++ b/Program.cs @@ -1,15 +1,11 @@ -using HnsExplorer.HostContainerServices; using System.Runtime.InteropServices; using Microsoft.Win32; -using System.Diagnostics; +using HnsExplorer.Data; namespace HnsExplorer; static class Program { - public static HnsAccess HnsAccess = new(); - public static HcsAccess HcsAccess = new(); - private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; @@ -40,7 +36,8 @@ static void Main() { ApplicationConfiguration.Initialize(); SetWindowTheme(); - var summaryForm = new SummaryForm(); + var datasource = new HnsDatasource(); + var summaryForm = new SummaryForm(datasource); SetTitleBarTheme(summaryForm.Handle); Application.Run(summaryForm); } @@ -62,7 +59,7 @@ private static bool CheckSystemLightMode() [DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); - private static void SetTitleBarTheme(IntPtr handle) + public static void SetTitleBarTheme(IntPtr handle) { if (!CheckSystemLightMode() && IsWindows10OrGreater(17763)) { diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..27a9992 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HnsExplorer.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HnsExplorer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap HnsExplorerIconLarge { + get { + object obj = ResourceManager.GetObject("HnsExplorerIconLarge", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..61a968b --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\HnsExplorerIconLarge.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Resources/HnsExplorerIconLarge.png b/Resources/HnsExplorerIconLarge.png new file mode 100644 index 0000000000000000000000000000000000000000..45d69315830213c34040da7c5439529314f730dd GIT binary patch literal 6629 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 literal 0 HcmV?d00001 diff --git a/SplashForm.Designer.cs b/SplashForm.Designer.cs new file mode 100644 index 0000000..35f6505 --- /dev/null +++ b/SplashForm.Designer.cs @@ -0,0 +1,111 @@ +using HnsExplorer.Extensions; + +namespace HnsExplorer +{ + partial class SplashForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + this.BackColor = Program.ACTIVE_COLOR_BACKGROUND_WINDOW; + this.label1.ForeColor = Program.ACTIVE_COLOR_FOREGROUND; + this.progressBar1.BackColor = Program.ACTIVE_COLOR_BACKGROUND_TEXTBOX; + this.progressBar1.ForeColor = Program.ACTIVE_COLOR_SEARCH_HIGHLIGHT; + this.timer1.Interval = 10; + this.timer1.Start(); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.progressBar1 = new HnsExplorer.Extensions.ProgressBarExtended(); + this.label1 = new System.Windows.Forms.Label(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // progressBar1 + // + this.progressBar1.Location = new System.Drawing.Point(75, 39); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(345, 23); + this.progressBar1.TabIndex = 0; + this.progressBar1.Click += new System.EventHandler(this.progressBar1_Click); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(75, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(345, 14); + this.label1.TabIndex = 1; + this.label1.Text = "Initialising..."; + this.label1.Click += new System.EventHandler(this.label1_Click); + // + // timer1 + // + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // pictureBox1 + // + this.pictureBox1.Image = global::HnsExplorer.Properties.Resources.HnsExplorerIconLarge; + this.pictureBox1.InitialImage = global::HnsExplorer.Properties.Resources.HnsExplorerIconLarge; + this.pictureBox1.Location = new System.Drawing.Point(11, 12); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(52, 52); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pictureBox1.TabIndex = 2; + this.pictureBox1.TabStop = false; + // + // SplashForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(433, 75); + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.progressBar1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SplashForm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "LoadingForm"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + private Label label1; + private System.Windows.Forms.Timer timer1; + private ProgressBarExtended progressBar1; + private PictureBox pictureBox1; + } +} \ No newline at end of file diff --git a/SplashForm.cs b/SplashForm.cs new file mode 100644 index 0000000..a074666 --- /dev/null +++ b/SplashForm.cs @@ -0,0 +1,39 @@ +using HnsExplorer.Data; + +namespace HnsExplorer +{ + public partial class SplashForm : Form + { + private HnsDatasource datasource; + public SplashForm(HnsDatasource datasource) + { + this.datasource = datasource; + InitializeComponent(); + } + + private void label1_Click(object sender, EventArgs e) + { + + } + + private void progressBar1_Click(object sender, EventArgs e) + { + + } + + private void timer1_Tick(object sender, EventArgs e) + { + label1.Text = datasource.LoadingState; + var p = datasource.NumberOfStepsLoaded; + var t = datasource.NumberOfStepsTotal; + var perc = (double)p / t * 100; + progressBar1.Value = (int)perc; + progressBar1.Update(); + if (p == t) + { + timer1.Stop(); + Close(); + } + } + } +} diff --git a/SplashForm.resx b/SplashForm.resx new file mode 100644 index 0000000..d731088 --- /dev/null +++ b/SplashForm.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/SummaryForm.Designer.cs b/SummaryForm.Designer.cs index f8cfdd4..30c0d81 100644 --- a/SummaryForm.Designer.cs +++ b/SummaryForm.Designer.cs @@ -125,7 +125,7 @@ private void InitializeComponent() this.richTextBox1.BackColor = System.Drawing.SystemColors.WindowText; this.richTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill; - this.richTextBox1.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.richTextBox1.Font = new System.Drawing.Font("Lucida Console", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.richTextBox1.ForeColor = System.Drawing.SystemColors.Window; this.richTextBox1.Location = new System.Drawing.Point(0, 0); this.richTextBox1.Name = "richTextBox1"; @@ -215,7 +215,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.SystemColors.WindowFrame; - this.ClientSize = new System.Drawing.Size(1125, 553); + this.ClientSize = new System.Drawing.Size(1234, 553); this.Controls.Add(this.button4); this.Controls.Add(this.button3); this.Controls.Add(this.button2); @@ -225,6 +225,7 @@ private void InitializeComponent() this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MinimumSize = new System.Drawing.Size(815, 0); this.Name = "SummaryForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Form1"; this.Load += new System.EventHandler(this.SummaryForm_Load); this.splitContainer1.Panel1.ResumeLayout(false); diff --git a/SummaryForm.cs b/SummaryForm.cs index 0bccdc9..cf8fec8 100644 --- a/SummaryForm.cs +++ b/SummaryForm.cs @@ -1,4 +1,4 @@ -using HnsExplorer.Extensions; +using HnsExplorer.Data; using System.Diagnostics; using System.Text.Json; using System.Text.Json.Serialization; @@ -7,10 +7,11 @@ namespace HnsExplorer; public partial class SummaryForm : Form { - private static string? EXPORT_DATA_SNAPSHOT; + private HnsDatasource datasource; - public SummaryForm() + public SummaryForm(HnsDatasource datasource) { + this.datasource = datasource; InitializeComponent(); LoadTreeview(); textBox1.KeyPress += new KeyPressEventHandler(CheckEnterKeyPress); @@ -20,77 +21,36 @@ public SummaryForm() private void LoadTreeview() { - - - var activitiesData = Program.HnsAccess.GetActivities(); - var namespaceData = Program.HnsAccess.GetNamespaces(); - var networkData = Program.HnsAccess.GetNetworks(); - var policyData = Program.HnsAccess.GetPolicyLists(); - var endpointData = Program.HnsAccess.GetEndpoints(); - var computeData = Program.HcsAccess.GetComputeSystems(); - - var summaryOutput = $"Activities: {activitiesData.Count()}{Environment.NewLine}"; - summaryOutput += $"Namespaces: {namespaceData.Count()}{Environment.NewLine}"; - summaryOutput += $"Networks: {networkData.Count()}{Environment.NewLine}"; - summaryOutput += $"Network policies: {policyData.Count()}{Environment.NewLine}"; - summaryOutput += $"Network endpoints: {endpointData.Count()}{Environment.NewLine}"; - summaryOutput += $"Compute systems: {computeData.Count()}{Environment.NewLine}"; - - richTextBox1.Text = summaryOutput; - - treeView1.Nodes.Clear(); - - var activitiesNode = new TreeNode - { - Text = "Activities", - Tag = summaryOutput - }; - var orphanedActivities = activitiesNode.Nodes.InsertNestedChildren(activitiesData, "ID", "parentId", "Activity", "Allocators.Tag"); - var orphanedNetworks = activitiesNode.Nodes.InsertNestedChildren(networkData, "ID", "Resources.ID", "Network", "Name"); - var orphanedEndpoints = activitiesNode.Nodes.InsertNestedChildren(endpointData, "ID", "Resources.ID", "Endpoint", "Name"); - var orphanedPolicies = activitiesNode.Nodes.InsertNestedChildren(policyData, "ID", "Resources.ID", "Policy", "Name"); - treeView1.Nodes.Add(activitiesNode); - - var orphansNode = new TreeNode("Orphaned Data"); - orphansNode.Nodes.InsertChildren(orphanedActivities, "ID", "Activities", "Allocators.Tag"); - orphansNode.Nodes.InsertChildren(orphanedNetworks, "ID", "Network", "Name"); - orphansNode.Nodes.InsertChildren(orphanedEndpoints, "ID", "Endpoint", "Name"); - orphansNode.Nodes.InsertChildren(orphanedPolicies, "ID", "Endpoint", "Name"); - - var endpointIds = new List(); - foreach (var item in endpointData) + datasource.Reset(); + Task.Run(() => { - var id = item.GetJsonDataAsString("ID"); - endpointIds.Add(id); - } - var endpointStatsData = Program.HnsAccess.GetEndpointStats(endpointIds); - activitiesNode.Nodes.InsertNestedChildren(endpointStatsData, "InstanceId", "EndpointId", "Endpoint Stats", "Name"); + var loadingForm = new SplashForm(datasource); + if (Visible) + { + var x = Location.X + (Width / 2) - (loadingForm.Width / 2); + var y = Location.Y + (Height / 2) - (loadingForm.Height / 2); + loadingForm.StartPosition = FormStartPosition.Manual; + loadingForm.Location = new Point(x, y); + } + else + { + loadingForm.StartPosition = FormStartPosition.CenterScreen; + } + Application.Run(loadingForm); + loadingForm.Dispose(); + }); + datasource.Load(); - var orphanedCompute = treeView1.Nodes.InsertChildrenWithMatchingParentReference(computeData, endpointData, "Id", "ID", "VirtualMachine", "Virtual Machine", "Owner"); - orphanedCompute = treeView1.Nodes.InsertChildrenWithMatchingParentReference(orphanedCompute, endpointData, "Id", "ID", "SharedContainers", "Container", "Owner"); - orphansNode.Nodes.InsertChildren(orphanedCompute, "ID", "Compute", "Name"); - var namespaceNode = new TreeNode - { - Text = "Namespaces", - Tag = $"{namespaceData.Count()} namespaces" - }; - namespaceNode.Nodes.InsertChildren(namespaceData, "ID", "Namespace", "CompartmentId"); - treeView1.Nodes.Add(namespaceNode); + richTextBox1.Text = datasource.SummaryOutput; + treeView1.Nodes.Clear(); + treeView1.Nodes.Add(datasource.ActivitiesNode); + treeView1.Nodes.Add(datasource.NamespacesNode); + treeView1.Nodes.Add(datasource.RoutesNode); - if (orphansNode.Nodes.Count > 0) + if (datasource.OrphansNode.Nodes.Count > 0) { - treeView1.Nodes.Add(orphansNode); + treeView1.Nodes.Add(datasource.OrphansNode); } - - var allData = new Dictionary>(); - allData.Add("Namespaces", namespaceData); - allData.Add("Network", networkData); - allData.Add("Policies", policyData); - allData.Add("Endpoints", endpointData); - allData.Add("Compute", computeData); - allData.Add("Activities", activitiesData); - allData.Add("EndpointStats", endpointStatsData); - EXPORT_DATA_SNAPSHOT = JsonSerializer.Serialize(allData); } private void button1_Click(object sender, EventArgs e) @@ -105,7 +65,7 @@ private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) if(e.Node is not null) { ClearSearchResults(treeView1.Nodes); - SearchRecursive(treeView1.Nodes, textBox1.Text); + SearchRecursive(treeView1.Nodes, textBox1.Text, false); e.Node.ForeColor = Program.ACTIVE_COLOR_HIGHLIGHT_FOREGROUND; e.Node.BackColor = Program.ACTIVE_COLOR_HIGHLIGHT_BACKGROUND; @@ -208,7 +168,7 @@ private bool SelectFirst(TreeNodeCollection nodes, string searchFor) return false; } - private bool SearchRecursive(TreeNodeCollection nodes, string searchFor) + private bool SearchRecursive(TreeNodeCollection nodes, string searchFor, bool expandToMakeVisible = true) { if (string.IsNullOrEmpty(searchFor)) { @@ -221,9 +181,12 @@ private bool SearchRecursive(TreeNodeCollection nodes, string searchFor) { node.ForeColor = Program.ACTIVE_COLOR_SEARCH_FOREGROUND; node.BackColor = Program.ACTIVE_COLOR_SEARCH_HIGHLIGHT; - node.EnsureVisible(); + if(expandToMakeVisible) + { + node.EnsureVisible(); + } } - if (SearchRecursive(node.Nodes, searchFor)) + if (SearchRecursive(node.Nodes, searchFor, expandToMakeVisible)) return true; } return false; @@ -258,7 +221,7 @@ private void button2_Click(object sender, EventArgs e) private void button3_Click(object sender, EventArgs e) { - if(EXPORT_DATA_SNAPSHOT is null) + if(datasource.ExportDataSnapshot is null) { richTextBox1.Text = $"There is no data available to export"; } @@ -268,7 +231,7 @@ private void button3_Click(object sender, EventArgs e) var filename = $"HnsExport_{DateTime.Now.ToFileTime()}.json"; var filePath = Path.Join(here, filename); TextWriter txt = new StreamWriter(filePath); - txt.Write(EXPORT_DATA_SNAPSHOT); + txt.Write(datasource.ExportDataSnapshot); txt.Close(); richTextBox1.Text = $"Data saved to {filePath}"; } @@ -286,6 +249,22 @@ private void button4_Click(object sender, EventArgs e) Write-Host -ForegroundColor White ' |_| ' Write-Host -ForegroundColor Cyan 'Using pktmon to create a packet capture' + $pktmon = Get-Command 'pktmon' -ErrorAction 'SilentlyContinue' + if($null -eq $pktmon) { + Write-Host -ForegroundColor Red 'The executable pktmon is not available in the PATH on this system' + Write-Host -ForegroundColor Red 'https://docs.microsoft.com/en-us/windows-server/networking/technologies/pktmon/pktmon' + Write-Host -ForegroundColor Red -NoNewline ""`nPress ENTER to close this window"" + Read-Host + exit + } + $pktmonBusy = pktmon status | Where-Object { $_ -notlike '*not running*' } + while ($pktmonBusy) { + Write-Warning 'There is already a pktmon trace running, try again when this one is complete' + Write-Host -ForegroundColor Yellow (Invoke-Expression 'pktmon status' | Out-String) + Write-Host -ForegroundColor Yellow -NoNewline ""`nPress ENTER to try again"" + Read-Host + $pktmonBusy = pktmon status | Where-Object { $_ -notlike '*not running*' } + } $interfaceList = pktmon list --json | ConvertFrom-Json | Foreach-Object { $_ | Select-Object -ExpandProperty Components ` | Where-Object { $_.Type -like '*vNIC*' -or $_.Type -eq 'NetVsc' } `