Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
51cc09f
Migrate Cores and Segments to using V2 APIs
epugh Jul 23, 2026
9ef7050
Finalize removing Cores in favour of CoresV2
epugh Jul 23, 2026
a29ef2b
Migrate Logs to v2 except the setLevel, which is v1.
epugh Jul 24, 2026
f4f6d1b
migrate to v2 for threads, plugins. Plugins used a raw prometheus ou…
epugh Jul 24, 2026
194e9c7
Back out metrics chnages (and plugins.js), they need more investigagion.
epugh Jul 24, 2026
b206b73
Migrate all of Collections to V2, except ClusterStatus which appears …
epugh Jul 24, 2026
4971a27
Migrate Paramsets to v2
epugh Jul 24, 2026
becbe1f
Ensure standalone/user-managed still works.
epugh Jul 24, 2026
be1e11f
Two lingering bugs in the Admin UI that reviewing htis PR flagged.
epugh Jul 24, 2026
a8cd6cc
More V2 API us for list collections and aliasss.
epugh Jul 24, 2026
dbc468d
Merge remote-tracking branch 'upstream/main' into migrate_admin_ui_to…
epugh Jul 29, 2026
bb577dd
Merge remote-tracking branch 'upstream/main' into migrate_admin_ui_to…
epugh Jul 29, 2026
88f14b9
Finish using SystemV2 throughout now.
epugh Jul 29, 2026
68c7aae
Migrate Segments to V2 api.
epugh Jul 29, 2026
9cfd2a9
Merge remote-tracking branch 'upstream/main' into migrate_admin_ui_to…
epugh Jul 29, 2026
b50670b
Fix reloadCore success/failure indicator not rendering
epugh Jul 29, 2026
7bbdcbb
Fix collections.js UI feedback flags and reloadCollection callback
epugh Jul 29, 2026
fa2da9b
Bug fix handling paramsets (race condition)
epugh Aug 1, 2026
38e6f3a
using playwright mcp for testing
epugh Aug 1, 2026
4e75449
similar to paramsets, the race condition on picking cloud or not...
epugh Aug 1, 2026
f2ea643
Make v2 superagent based apis and v1 $http angular both emit errors i…
epugh Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ GEMINI.md
# WANT TO ADD MORE? You can tell Git without adding to this file:
# See https://git-scm.com/docs/gitignore
# In particular, if you have tools you use, add to $GIT_DIR/info/exclude or use core.excludesFile
/.playwright-mcp
160 changes: 86 additions & 74 deletions solr/webapp/web/js/angular/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ solrAdminApp.config([
};
});

solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $location, Cores, Collections, System, Ping, Constants, SchemaDesigner) {
solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $location, $timeout, CoresV2, CollectionsV2, AliasesV2, SystemV2, Ping, Constants, SchemaDesigner, ApiErrorHandler) {

$rootScope.exceptions={};

Expand All @@ -516,86 +516,98 @@ solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $

$scope.refresh();
$scope.resetMenu = function(page, pageType) {
Cores.list(function(data) {
$scope.cores = [];
var currentCoreName = $route.current.params.core;
delete $scope.currentCore;
for (key in data.status) {
var core = data.status[key];
if (core.name.startsWith("._designer_")) {
continue;
}
$scope.cores.push(core);
if ((!$scope.isSolrCloud || pageType == Constants.IS_CORE_PAGE) && core.name == currentCoreName) {
$scope.currentCore = core;
CoresV2.getAllCoreStatus({indexInfo: false}, function(error, data, response) {
$timeout(function() {
if (error) { ApiErrorHandler.handle(response); return; }
$scope.cores = [];
var currentCoreName = $route.current.params.core;
delete $scope.currentCore;
for (key in data.status) {
var core = data.status[key];
if (core.name.startsWith("._designer_")) {
continue;
}
$scope.cores.push(core);
if ((!$scope.isSolrCloud || pageType == Constants.IS_CORE_PAGE) && core.name == currentCoreName) {
$scope.currentCore = core;
}
}
}
$scope.showInitFailures = Object.keys(data.initFailures).length>0;
$scope.initFailures = data.initFailures;
$scope.showInitFailures = Object.keys(data.initFailures).length>0;
$scope.initFailures = data.initFailures;
});
});

System.get(function(data) {
$scope.isCloudEnabled = data.mode.match( /solrcloud/i );
$scope.usersPermissions = data.security.permissions;
$scope.isSecurityEnabled = $scope.authenticationPlugin != null;

$scope.isSchemaDesignerEnabled = $scope.isPermitted([
permissions.CONFIG_EDIT_PERM,
permissions.SCHEMA_EDIT_PERM,
permissions.READ_PERM,
permissions.UPDATE_PERM
]);

var currentCollectionName = $route.current.params.core;
delete $scope.currentCollection;
if ($scope.isCloudEnabled) {
Collections.list(function (cdata) {
Collections.listaliases(function (adata) {
$scope.aliases = [];
for (var key in adata.aliases) {
props = {};
if (key in adata.properties) {
props = adata.properties[key];
}
var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props};
$scope.aliases.push(alias);
if (pageType == Constants.IS_COLLECTION_PAGE && alias.name == currentCollectionName) {
$scope.currentCollection = alias;
}
}
$scope.collections = [];
for (key in cdata.collections) {
if (cdata.collections[key].startsWith("._designer_")) {
continue; // ignore temp designer collections
}
var collection = {name: cdata.collections[key], type: 'collection'};
$scope.collections.push(collection);
if (pageType == Constants.IS_COLLECTION_PAGE && collection.name == currentCollectionName) {
$scope.currentCollection = collection;
}
}
SystemV2.getNodeSystemInfo({}, function(error, data, response) {
$timeout(function() {
if (error) { ApiErrorHandler.handle(response); return; }
$scope.isCloudEnabled = data.mode.match( /solrcloud/i );
$scope.usersPermissions = data.security.permissions;
$scope.isSecurityEnabled = $scope.authenticationPlugin != null;

$scope.isSchemaDesignerEnabled = $scope.isPermitted([
permissions.CONFIG_EDIT_PERM,
permissions.SCHEMA_EDIT_PERM,
permissions.READ_PERM,
permissions.UPDATE_PERM
]);

var currentCollectionName = $route.current.params.core;
delete $scope.currentCollection;
if ($scope.isCloudEnabled) {
CollectionsV2.listCollections(function (error, cdata, response) {
$timeout(function() {
if (error) { ApiErrorHandler.handle(response); return; }
AliasesV2.getAliases(function (error, adata, response) {
$timeout(function() {
if (error) { ApiErrorHandler.handle(response); return; }
$scope.aliases = [];
for (var key in adata.aliases) {
props = {};
if (key in adata.properties) {
props = adata.properties[key];
}
var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props};
$scope.aliases.push(alias);
if (pageType == Constants.IS_COLLECTION_PAGE && alias.name == currentCollectionName) {
$scope.currentCollection = alias;
}
}
$scope.collections = [];
for (key in cdata.collections) {
if (cdata.collections[key].startsWith("._designer_")) {
continue; // ignore temp designer collections
}
var collection = {name: cdata.collections[key], type: 'collection'};
$scope.collections.push(collection);
if (pageType == Constants.IS_COLLECTION_PAGE && collection.name == currentCollectionName) {
$scope.currentCollection = collection;
}
}

$scope.aliases_and_collections = $scope.aliases;
if ($scope.aliases.length > 0) {
$scope.aliases_and_collections = $scope.aliases_and_collections.concat({name:'-----'});
}
$scope.aliases_and_collections = $scope.aliases_and_collections.concat($scope.collections);
$scope.aliases_and_collections = $scope.aliases;
if ($scope.aliases.length > 0) {
$scope.aliases_and_collections = $scope.aliases_and_collections.concat({name:'-----'});
}
$scope.aliases_and_collections = $scope.aliases_and_collections.concat($scope.collections);
});
});
});
});
});
}

$scope.showEnvironment = data.environment !== undefined;
if (data.environment) {
$scope.environment = data.environment;
var env_labels = {'prod': 'Production', 'stage': 'Staging', 'test': 'Test', 'dev': 'Development'};
$scope.environment_label = env_labels[data.environment];
if (data.environment_label) {
$scope.environment_label = data.environment_label;
}
if (data.environment_color) {
$scope.environment_color = data.environment_color;

$scope.showEnvironment = data.environment !== undefined;
if (data.environment) {
$scope.environment = data.environment;
var env_labels = {'prod': 'Production', 'stage': 'Staging', 'test': 'Test', 'dev': 'Development'};
$scope.environment_label = env_labels[data.environment];
if (data.environment_label) {
$scope.environment_label = data.environment_label;
}
if (data.environment_color) {
$scope.environment_color = data.environment_color;
}
}
}
});
});

$scope.showingLogging = page.lastIndexOf("logging", 0) === 0;
Expand Down
88 changes: 48 additions & 40 deletions solr/webapp/web/js/angular/controllers/cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

solrAdminApp.controller('CloudController',
function($scope, $location, Zookeeper, Constants, Collections, System, Metrics, MetricsExtractor, ZookeeperStatus) {
function($scope, $location, $timeout, Zookeeper, Constants, Collections, SystemV2, Metrics, MetricsExtractor, ZookeeperStatus, ApiErrorHandler) {

$scope.showDebug = false;

Expand All @@ -37,7 +37,7 @@ solrAdminApp.controller('CloudController',
graphSubController($scope, Zookeeper, false);
} else if (view === "nodes") {
$scope.resetMenu("cloud-nodes", Constants.IS_ROOT_PAGE);
nodesSubController($scope, Collections, System, Metrics, MetricsExtractor);
nodesSubController($scope, $timeout, Collections, SystemV2, Metrics, MetricsExtractor, ApiErrorHandler);
} else if (view === "zkstatus") {
$scope.resetMenu("cloud-zkstatus", Constants.IS_ROOT_PAGE);
zkStatusSubController($scope, ZookeeperStatus, false);
Expand Down Expand Up @@ -107,7 +107,7 @@ function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}

var nodesSubController = function($scope, Collections, System, Metrics, MetricsExtractor) {
var nodesSubController = function($scope, $timeout, Collections, SystemV2, Metrics, MetricsExtractor, ApiErrorHandler) {
$scope.pageSize = 10;
$scope.showNodes = true;
$scope.showTree = false;
Expand Down Expand Up @@ -353,43 +353,51 @@ var nodesSubController = function($scope, Collections, System, Metrics, MetricsE
Fetch system info for all selected nodes
Pick the data we want to display and add it to the node-centric data structure
*/
System.get({"nodes": liveNodesToShow.join(',')}, function (systemResponse) {
for (var node in systemResponse) {
if (node in nodes) {
var s = systemResponse[node];
nodes[node]['system'] = s;
var memTotal = s.system.totalPhysicalMemorySize;
var memFree = s.system.freePhysicalMemorySize;
var memPercentage = Math.floor((memTotal - memFree) / memTotal * 100);
nodes[node]['memUsedPct'] = memPercentage;
nodes[node]['memUsedPctStyle'] = styleForPct(memPercentage);
nodes[node]['memTotal'] = bytesToSize(memTotal);
nodes[node]['memFree'] = bytesToSize(memFree);
nodes[node]['memUsed'] = bytesToSize(memTotal - memFree);

var heapMax = s.jvm.memory.raw.max;
var heapTotal = s.jvm.memory.raw.total;
var heapFree = s.jvm.memory.raw.free;
var heapPercentage = Math.floor((heapTotal - heapFree) / heapMax * 100);
nodes[node]['heapUsed'] = bytesToSize(heapTotal - heapFree);
nodes[node]['heapUsedPct'] = heapPercentage;
nodes[node]['heapUsedPctStyle'] = styleForPct(heapPercentage);
nodes[node]['heapMax'] = bytesToSize(heapMax);
nodes[node]['heapTotal'] = bytesToSize(heapTotal);
nodes[node]['heapFree'] = bytesToSize(heapFree);

var jvmUptime = s.jvm.jmx.upTimeMS / 1000; // Seconds
nodes[node]['jvmUptime'] = secondsForHumans(jvmUptime);
nodes[node]['jvmUptimeSec'] = jvmUptime;

nodes[node]['uptime'] = (s.system.uptime || "unknown").replace(/.*up (.*?,.*?),.*/, "$1");
nodes[node]['loadAvg'] = Math.round(s.system.systemLoadAverage * 100) / 100;
nodes[node]['cpuPct'] = Math.ceil(s.system.processCpuLoad * 100);
nodes[node]['cpuPctStyle'] = styleForPct(Math.ceil(s.system.processCpuLoad));
nodes[node]['maxFileDescriptorCount'] = s.system.maxFileDescriptorCount;
nodes[node]['openFileDescriptorCount'] = s.system.openFileDescriptorCount;
}
SystemV2.getNodeSystemInfo({"nodes": liveNodesToShow.join(',')}, function (error, data, response) {
if (error) {
console.error('Failed to fetch node system info:', error);
ApiErrorHandler.handle(response);
return;
}
$timeout(function() {
var systemResponse = response.body;
for (var node in systemResponse) {
if (node in nodes) {
var s = systemResponse[node];
nodes[node]['system'] = s;
var memTotal = s.system.totalPhysicalMemorySize;
var memFree = s.system.freePhysicalMemorySize;
var memPercentage = Math.floor((memTotal - memFree) / memTotal * 100);
nodes[node]['memUsedPct'] = memPercentage;
nodes[node]['memUsedPctStyle'] = styleForPct(memPercentage);
nodes[node]['memTotal'] = bytesToSize(memTotal);
nodes[node]['memFree'] = bytesToSize(memFree);
nodes[node]['memUsed'] = bytesToSize(memTotal - memFree);

var heapMax = s.jvm.memory.raw.max;
var heapTotal = s.jvm.memory.raw.total;
var heapFree = s.jvm.memory.raw.free;
var heapPercentage = Math.floor((heapTotal - heapFree) / heapMax * 100);
nodes[node]['heapUsed'] = bytesToSize(heapTotal - heapFree);
nodes[node]['heapUsedPct'] = heapPercentage;
nodes[node]['heapUsedPctStyle'] = styleForPct(heapPercentage);
nodes[node]['heapMax'] = bytesToSize(heapMax);
nodes[node]['heapTotal'] = bytesToSize(heapTotal);
nodes[node]['heapFree'] = bytesToSize(heapFree);

var jvmUptime = s.jvm.jmx.upTimeMS / 1000; // Seconds
nodes[node]['jvmUptime'] = secondsForHumans(jvmUptime);
nodes[node]['jvmUptimeSec'] = jvmUptime;

nodes[node]['uptime'] = (s.system.uptime || "unknown").replace(/.*up (.*?,.*?),.*/, "$1");
nodes[node]['loadAvg'] = Math.round(s.system.systemLoadAverage * 100) / 100;
nodes[node]['cpuPct'] = Math.ceil(s.system.processCpuLoad * 100);
nodes[node]['cpuPctStyle'] = styleForPct(Math.ceil(s.system.processCpuLoad));
nodes[node]['maxFileDescriptorCount'] = s.system.maxFileDescriptorCount;
nodes[node]['openFileDescriptorCount'] = s.system.openFileDescriptorCount;
}
}
});
});

/*
Expand Down Expand Up @@ -792,7 +800,7 @@ var graphSubController = function ($scope, Zookeeper) {
params.filter = filter;
}

Zookeeper.clusterState(params, function (data) {
Zookeeper.clusterState(params, function (data) {
var state = data.znode.data;
var leaf_count = 0;
var graph_data = {
Expand Down
Loading
Loading