From 898cc22b22b5d800d6f307fec64efb8ebd782071 Mon Sep 17 00:00:00 2001 From: Vincent Sylvester Date: Mon, 17 Feb 2025 19:35:32 +0100 Subject: [PATCH] Grid Stats as part of GridInfoService Signed-off-by: UbitUmarov --- .../Server/Handlers/Grid/GridInfoHandlers.cs | 159 +++++++++++++++++- .../Grid/GridInfoServerInConnector.cs | 10 ++ bin/Robust.HG.ini.example | 3 + bin/Robust.ini.example | 3 + 4 files changed, 174 insertions(+), 1 deletion(-) diff --git a/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs index f6416979a4..ff0c78fdf3 100644 --- a/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs +++ b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs @@ -38,6 +38,8 @@ using OpenSim.Framework; using OpenSim.Framework.Servers.HttpServer; using OpenMetaverse; using OpenMetaverse.StructuredData; +using OpenSim.Data; +using OpenSim.Services.Base; namespace OpenSim.Server.Handlers.Grid { @@ -46,8 +48,15 @@ namespace OpenSim.Server.Handlers.Grid private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IConfigSource m_Config; private Dictionary _info = new Dictionary(); + private Dictionary _stats = new Dictionary(); private byte[] cachedJsonAnswer = null; private byte[] cachedRestAnswer = null; + private byte[] cachedStatAnswer = null; + private bool stats_available = false; + private int _lastrun; + protected IRegionData m_Database_regions = null; + protected IGridUserData m_Database_griduser = null; + /// /// Instantiate a GridInfoService object. /// @@ -67,10 +76,47 @@ namespace OpenSim.Server.Handlers.Grid private void loadGridInfo(IConfigSource configSource) { + IConfig gridCfg = configSource.Configs["GridInfoService"]; + + stats_available = !gridCfg.GetBoolean("DisableStatsEndpoint", false); + + if (stats_available) + { + IConfig dbConfig = configSource.Configs["DatabaseService"]; + if (dbConfig is not null) + { + ServiceBase serviceBase = new(configSource); + + string dllName = String.Empty; + string connString = String.Empty; + + if (dllName.Length == 0) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (connString.Length == 0) + connString = dbConfig.GetString("ConnectionString", String.Empty); + + if (dllName.Length != 0 && connString.Length != 0) + { + m_Database_regions = serviceBase.LoadPlugin(dllName, [connString, "regions"]); + m_Database_griduser = serviceBase.LoadPlugin(dllName, [connString, "GridUser"]); + } + + if (m_Database_griduser != null && m_Database_regions != null) + { + stats_available = true; + _log.Debug("[GRID INFO SERVICE]: Grid Stats enabled"); + GetGridStats(); + } + } + if (!stats_available) + { + _log.Warn("[GRID INFO SERVICE]: Could not find or initialize Database Service config, grid stats will be unavailable!"); + } + } + _info["platform"] = "OpenSim"; try { - IConfig gridCfg = configSource.Configs["GridInfoService"]; if (gridCfg != null) { foreach (string k in gridCfg.GetKeys()) @@ -224,5 +270,116 @@ namespace OpenSim.Server.Handlers.Grid httpResponse.ContentType = "application/json"; httpResponse.RawBuffer = cachedJsonAnswer; } + + public void GetGridStats() + { + int region_count = 0; + int active_users = 0; + int residents = 0; + + _stats["region_count"] = region_count.ToString(); + _stats["active_users"] = active_users.ToString(); + _stats["residents"] = residents.ToString(); + + int epoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + _lastrun = epoch; + + try + { + // Fetch region data + List regions = m_Database_regions.GetOnlineRegions(UUID.Zero); + // Fetch all grid users, can't do a simple query unfortunately + GridUserData[] gridusers = m_Database_griduser.GetAll(string.Empty); + + // Go through grid user data + foreach (GridUserData griduser in gridusers) + { + // Don't count if uui + if (!griduser.UserID.Contains(';')) + residents++; + + griduser.Data.TryGetValue("Login", out string login); + if (int.TryParse(login, out int last_login)) + { + if (last_login == 0) + continue; + + // Count if last login was within the last 30 days + if (last_login > (epoch - 2592000)) + active_users++; + } + } + + foreach (RegionData region in regions) + { + // Count individual region equivalent + region_count += (region.sizeX / 256) * (region.sizeY / 256); + } + + _stats["residents"] = residents.ToString(); + _stats["active_users"] = active_users.ToString(); + _stats["region_count"] = region_count.ToString(); + } + catch (Exception ex) + { + _log.ErrorFormat("[GRID INFO SERVICE]: Could not fetch grid stats: {0}", ex.Message); + } + } + + public XmlRpcResponse GridStatsHandler(XmlRpcRequest request, IPEndPoint remoteClient) + { + XmlRpcResponse response = new(); + Hashtable responseData = []; + + // Only fetch new stats if the last run is 5 minutes old since this is heavy db stuff + int Epoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + if ((Epoch - 300) > _lastrun) + GetGridStats(); + + _log.DebugFormat("[GRID INFO SERVICE]: Request for grid stats from {0}", remoteClient.Address.ToString()); + + foreach (KeyValuePair k in _stats) + { + responseData[k.Key] = k.Value; + } + response.Value = responseData; + + return response; + + } + + public void RestGridStatsHandler(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + httpResponse.KeepAlive = false; + if (httpRequest.HttpMethod != "GET" || !stats_available) + { + httpResponse.StatusCode = (int)HttpStatusCode.MethodNotAllowed; + return; + } + + // Only fetch new stats if the last run is 5 minutes old since this is heavy db stuff + int Epoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + + if (cachedStatAnswer == null || (Epoch - 300) > _lastrun) + { + GetGridStats(); + osUTF8 osb = OSUTF8Cached.Acquire(); + osb.AppendASCII(""); + foreach (KeyValuePair k in _stats) + { + osb.AppendASCII('<'); + osb.AppendASCII(k.Key); + osb.AppendASCII('>'); + osb.AppendASCII(SecurityElement.Escape(k.Value.ToString())); + osb.AppendASCII("'); + } + osb.AppendASCII(""); + cachedStatAnswer = OSUTF8Cached.GetArrayAndRelease(osb); + } + httpResponse.ContentType = "application/xml"; + httpResponse.RawBuffer = cachedStatAnswer; + } } } diff --git a/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs b/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs index b601d3b8fc..0ec3506053 100644 --- a/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs +++ b/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs @@ -46,11 +46,21 @@ namespace OpenSim.Server.Handlers.Grid { GridInfoHandlers handlers = new GridInfoHandlers(config); + IConfig gridCfg = config.Configs["GridInfoService"]; + + bool stats_disabled = gridCfg.GetBoolean("DisableStatsEndpoint", false); + server.AddSimpleStreamHandler(new SimpleStreamHandler("/get_grid_info", handlers.RestGetGridInfoMethod)); server.AddSimpleStreamHandler(new SimpleStreamHandler("/json_grid_info", handlers.JsonGetGridInfoMethod)); server.AddXmlRPCHandler("get_grid_info", handlers.XmlRpcGridInfoMethod, false); + + if (!stats_disabled) + { + server.AddSimpleStreamHandler(new SimpleStreamHandler("/get_grid_stats", handlers.RestGridStatsHandler)); + server.AddXmlRPCHandler("get_grid_stats", handlers.GridStatsHandler, false); + } } } } diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index bd78316da6..0c8089f8c1 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -687,6 +687,9 @@ ; optional web page for profiles ;[AGENT_NAME] will be converted to Firstname.LastName by viewers ; web_profile_url = http://webprofilesurl:ItsPort?name=[AGENT_NAME] + + ; By default a xmlrpc handler outputs some grid statistics via /get_grid_stats This can be turned off here + ;DisableStatsEndpoint = true [GatekeeperService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:GatekeeperService" diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 2e37861c94..b7f4c86361 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -604,6 +604,9 @@ ; optional web page for profiles ;[AGENT_NAME] will be converted to Firstname.LastName by viewers ; web_profile_url = http://webprofilesurl:ItsPort?name=[AGENT_NAME] + + ; By default a xmlrpc handler outputs some grid statistics via /get_grid_stats This can be turned off here + ;DisableStatsEndpoint = true [Messaging] ; OfflineIM