Skip to content

Instantly share code, notes, and snippets.

@nirvincricut
Created July 28, 2021 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nirvincricut/fc222553e1ac6ed4ad00178c104b41c1 to your computer and use it in GitHub Desktop.
Save nirvincricut/fc222553e1ac6ed4ad00178c104b41c1 to your computer and use it in GitHub Desktop.
Search Grafana dashboards for keyword (.NET Core 3.1)
using System.Collections.Generic;
namespace GrafanaQuery
{
public class DashboardContent
{
public List<PanelInfo> Panels { get; set; }
}
}
using Newtonsoft.Json; //include this NuGet package (12.0.3)
namespace GrafanaQuery
{
public class DashboardInfo
{
[JsonProperty]
public int Id { get; set; }
[JsonIgnore]
public int CurrentVersionNumber { get; set; }
[JsonProperty]
public string Title { get; set; }
[JsonIgnore]
public string TopEditorUserName { get; set; }
}
}
using Newtonsoft.Json;
namespace GrafanaQuery
{
public class PanelInfo
{
[JsonProperty("datasource")]
public string DataSourceName { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Security.Principal; //NuGet package System.Security.Principal.Windows 5.0.0
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http; //include this NuGet package (3.0.1)
namespace GrafanaQuery
{
internal static class Program
{
private static string _password;
private static string LoginName => WindowsIdentity.GetCurrent().Name.Substring(11);
private static readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private static StreamWriter _fileWriter;
private static string GrafanaHost => Environment.GetCommandLineArgs().Skip(1).First();
private static string SearchTerm => Environment.GetCommandLineArgs().Skip(1).ToList()[1];
private static async Task Main()
{
Console.CancelKeyPress += OnCancelKeyPress;
try
{
LoadPassword();
string fileName = $"{Path.GetTempFileName()}.txt";
_fileWriter = new StreamWriter(File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read));
await RunQuery();
Console.WriteLine($"Results file: {fileName}");
}
catch (TaskCanceledException)
{
Console.WriteLine("*** Stopped by user");
Environment.ExitCode = 0;
}
catch (Exception exception)
{
Console.WriteLine(exception);
Environment.ExitCode = -1;
}
finally
{
await _fileWriter.DisposeAsync();
_fileWriter = null;
}
}
private static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs eventArgs)
{
eventArgs.Cancel = true;
_cancellationTokenSource.Cancel();
}
private static void LoadPassword()
{
Console.Write($"Enter password for {LoginName}: ");
_password = ReadPassword();
Console.WriteLine();
}
private static async Task RunQuery()
{
List<DashboardInfo> dashboards = await GetAllDashboards();
for (int i = 0; i < dashboards.Count; i++)
{
DashboardInfo dashboardInfo = dashboards[i];
Console.WriteLine($"Querying {i + 1} of {dashboards.Count}...");
List<VersionInfo> dashboardVersions = await GetDashboardVersions(dashboardInfo.Id);
dashboardInfo.TopEditorUserName = GetTopEditorUserNameFrom(dashboardVersions);
dashboardInfo.CurrentVersionNumber = dashboardVersions.OrderByDescending(x => x.EditDate).First().VersionNumber;
await PrintDataSourceInfo(dashboardInfo);
}
}
//https://stackoverflow.com/a/3404522/1687106
private static string ReadPassword()
{
var pass = string.Empty;
ConsoleKey key;
do
{
ConsoleKeyInfo keyInfo = Console.ReadKey(intercept: true);
key = keyInfo.Key;
if (key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write("\b \b");
pass = pass[..^1];
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write("*");
pass += keyInfo.KeyChar;
}
} while (key != ConsoleKey.Enter);
return pass;
}
private static async Task<List<DashboardInfo>> GetAllDashboards()
{
return await GetGrafanaObject<List<DashboardInfo>>($"{GrafanaHost}/api/search?type=dash-db");
}
private static async Task<List<VersionInfo>> GetDashboardVersions(int dashboardId)
{
return await GetGrafanaObject<List<VersionInfo>>($"{GrafanaHost}/api/dashboards/id/{dashboardId}/versions");
}
private static string GetTopEditorUserNameFrom(List<VersionInfo> dashboardVersions)
{
return
dashboardVersions
.GroupBy(x => x.UserName)
.Select(editStats => new
{
EditorUserName = editStats.Key,
TotalEditCount = editStats.Count()
})
.OrderByDescending(x => x.TotalEditCount)
.FirstOrDefault()?
.EditorUserName;
}
[SuppressMessage("ReSharper", "StringLiteralTypo")]
private static async Task PrintDataSourceInfo(DashboardInfo dashboardInfo)
{
List<string> dataSourceNames = await GetDataSourceNames(dashboardInfo.Id, dashboardInfo.CurrentVersionNumber);
if (dataSourceNames.Exists(d => d.Equals(SearchTerm, StringComparison.InvariantCultureIgnoreCase)))
{
await PrintLine($"\t{dashboardInfo.Title} (by {dashboardInfo.TopEditorUserName})");
}
}
[SuppressMessage("ReSharper", "TooManyDeclarations")]
private static async Task<List<string>> GetDataSourceNames(int dashboardId, int versionNumber)
{
List<PanelInfo> panels = await GetDashboardPanels(dashboardId, versionNumber);
return panels != null
? panels
.Select(panelInfo => panelInfo.DataSourceName?.Trim())
.Where(dataSourceName => !string.IsNullOrWhiteSpace(dataSourceName))
.Distinct()
.OrderBy(d => d)
.ToList()
: new List<string>();
}
private static async Task<List<PanelInfo>> GetDashboardPanels(int dashboardId, int versionNumber)
{
VersionInfo dashboardContent = await GetDashboardContent(dashboardId, versionNumber);
return dashboardContent?.Data?.Panels;
}
private static async Task<VersionInfo> GetDashboardContent(int dashboardId, int versionNumber)
{
return await GetGrafanaObject<VersionInfo>($"{GrafanaHost}/api/dashboards/id/{dashboardId}/versions/{versionNumber}");
}
private static async Task<T> GetGrafanaObject<T>(string url)
{
return await url
.WithBasicAuth(LoginName, _password)
.GetJsonAsync<T>(_cancellationTokenSource.Token);
}
private static async Task PrintLine(string message)
{
Console.WriteLine(message);
await _fileWriter.WriteLineAsync(message.ToCharArray(), _cancellationTokenSource.Token);
await _fileWriter.FlushAsync();
}
}
}
using System;
using Newtonsoft.Json;
namespace GrafanaQuery
{
public class VersionInfo
{
[JsonProperty("id")]
public int VersionId { get; set; }
[JsonProperty("version")]
public int VersionNumber { get; set; }
[JsonProperty("Created")]
public DateTime EditDate { get; set; }
[JsonProperty("createdBy")]
public string UserName { get; set; }
public DashboardContent Data { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment