You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ozone.apache.org by "navinko (via GitHub)" <gi...@apache.org> on 2026/04/13 20:49:08 UTC
[PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
navinko opened a new pull request, #10076:
URL: https://github.com/apache/ozone/pull/10076
## What changes were proposed in this pull request?
Implemented globalSearch for Node Status Search bar.
'HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows '
In the existing design, the search box on the SCM Web UI "Node Status" table had a page-level filter where we could only search across the currently visible rows governed by 'Show' dropdown (e.g., 10 ,20, 50 , All nodes at a time). Finding a specific datanode in a large cluster requires :
1: Either change the 'Show' dropdown to have more records in the table manually + search -> If target match found Ok else clicking through multiple pages in (previous - next ) fashion until the target node appeared on the active page.
2: Keep searching on the table with default size 10
Proposed Changes:
This PR introduces a global search mechanism to the SCM Web UI. The search input now filters against the entire master dataset of datanodes before the pagination slicing occurs, allowing users to instantly find nodes regardless of which page they originally resided on. Also it shows the pagination .
## What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-15007
## How was this patch tested?
- **Validated the existing implementation first **
-> page-1
<img width="1434" height="840" alt="image" src="https://github.com/user-attachments/assets/236b1506-4a9d-4aa7-8d1b-76583212c254" />
-> page-2
<img width="1429" height="869" alt="image" src="https://github.com/user-attachments/assets/fed02180-f6c6-4384-bdc6-f7e4bfb9c9a4" />
-> Search "ozone-ha-datanode-7.ozone-ha_default" UUID on serach bar at Page-1
<img width="2898" height="422" alt="image" src="https://github.com/user-attachments/assets/8f9ee3a9-dbc4-4c64-bec6-b344c61b7107" />
If we could have search on page-2, we have the target match , and since have one more records on 3rd page next navigation is enable.
<img width="2812" height="536" alt="image" src="https://github.com/user-attachments/assets/cdbf63cb-7a89-4748-8072-5cc0a1b5326a" />
</body>
</html>
- **Validated Proposed Implementation**
Scaled the cluster to 21 datanodes using docker-compose up --scale datanode=21 to force multiple pages of records.
Navigated to Page 1 and searched for a datanode known to be located on Page 2. Verified that the node immediately appeared in the table. Verified that the pagination text dynamically updated to reflect the search results (e.g., "Showing 1 to 1 of the total 1 entries.") and that the Next/Previous buttons disabled appropriately to prevent navigating to empty pages.
Cleared the search box and verified that the table restored the full cluster view and correct pagination.
-> page-1
<img width="1470" height="839" alt="1 page-1" src="https://github.com/user-attachments/assets/95e82caf-c2a1-4179-87a1-dfa112aeab43" />
-> page-2 (taken ozone-ha-datanode-19.ozone-ha_default for global serach)
<img width="1389" height="780" alt="2 Page-2" src="https://github.com/user-attachments/assets/11d90720-2aef-43fa-aec5-09575c7d00f4" />
-> page-3
<img width="1397" height="266" alt="3 Page-3" src="https://github.com/user-attachments/assets/aad64e68-706e-400f-bc64-ec1c60d42536" />
-> global serach found! (ozone-ha-datanode-19.ozone-ha_default -found)
<img width="1411" height="302" alt="GlobalSearch-Found" src="https://github.com/user-attachments/assets/cebec94f-a0bb-411e-9dec-a09b1481aac1" />
-> prev, next navigation disabled
<img width="1423" height="288" alt="globalsearch_prev_next-disabled" src="https://github.com/user-attachments/assets/24b4bf48-67e3-478e-ac59-650d67fbc73d" />
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "smengcl (via GitHub)" <gi...@apache.org>.
smengcl merged PR #10076:
URL: https://github.com/apache/ozone/pull/10076
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "navinko (via GitHub)" <gi...@apache.org>.
navinko commented on PR #10076:
URL: https://github.com/apache/ozone/pull/10076#issuecomment-4432754796
Thanks @smengcl for the review and merge.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "smengcl (via GitHub)" <gi...@apache.org>.
smengcl commented on PR #10076:
URL: https://github.com/apache/ozone/pull/10076#issuecomment-4427682734
Thanks @navinko for the patch.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "smengcl (via GitHub)" <gi...@apache.org>.
smengcl commented on code in PR #10076:
URL: https://github.com/apache/ozone/pull/10076#discussion_r3199108286
##########
hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js:
##########
@@ -218,28 +221,48 @@
$scope.statistics.containers.health.open_without_pipeline = ctrl.scmcontainermanager.OpenContainersWithoutPipeline;
});
- /*if option is 'All' display all records else display specified record on page*/
- $scope.UpdateRecordsToShow = () => {
- if($scope.RecordsToDisplay == 'All') {
- $scope.lastIndex = 1;
- $scope.nodeStatus = nodeStatusCopy;
+ /* Global Search Logic */
+ $scope.applyGlobalSearch = function() {
+ if (!$scope.search || $scope.search.trim() === "") {
+ // Reset to full list if search is empty
+ $scope.filteredNodes = [...nodeStatusCopy];
} else {
- $scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
- }
- $scope.currentPage = 1;
- }
- /* Page Slicing logic */
- $scope.handlePagination = (pageIndex, isDisabled) => {
- if(!isDisabled && $scope.RecordsToDisplay != 'All') {
- pageIndex = parseInt(pageIndex);
- let startIndex = 0, endIndex = 0;
- $scope.currentPage = pageIndex;
- startIndex = ($scope.currentPage - 1) * parseInt($scope.RecordsToDisplay);
- endIndex = startIndex + parseInt($scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(startIndex, endIndex);
+ let query = $scope.search.toLowerCase();
+ // Filter the master list
+ $scope.filteredNodes = nodeStatusCopy.filter(function(node) {
+ return (node.hostname && node.hostname.toLowerCase().includes(query)) ||
+ (node.opstate && node.opstate.toLowerCase().includes(query)) ||
+ (node.comstate && node.comstate.toLowerCase().includes(query)) ||
+ (node.uuid && node.uuid.toLowerCase().includes(query));
+ });
Review Comment:
This should also cover other fields like Used Space Percent / Capacity / Last Heartbeat to match original search behavior?
Or even better for maintenance: is it possible to match the whole row without having to specify each in case we add new columns in the future but forget to change the code here?
##########
hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js:
##########
@@ -218,28 +221,48 @@
$scope.statistics.containers.health.open_without_pipeline = ctrl.scmcontainermanager.OpenContainersWithoutPipeline;
});
- /*if option is 'All' display all records else display specified record on page*/
- $scope.UpdateRecordsToShow = () => {
- if($scope.RecordsToDisplay == 'All') {
- $scope.lastIndex = 1;
- $scope.nodeStatus = nodeStatusCopy;
+ /* Global Search Logic */
+ $scope.applyGlobalSearch = function() {
+ if (!$scope.search || $scope.search.trim() === "") {
+ // Reset to full list if search is empty
+ $scope.filteredNodes = [...nodeStatusCopy];
} else {
- $scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
- }
- $scope.currentPage = 1;
- }
- /* Page Slicing logic */
- $scope.handlePagination = (pageIndex, isDisabled) => {
- if(!isDisabled && $scope.RecordsToDisplay != 'All') {
- pageIndex = parseInt(pageIndex);
- let startIndex = 0, endIndex = 0;
- $scope.currentPage = pageIndex;
- startIndex = ($scope.currentPage - 1) * parseInt($scope.RecordsToDisplay);
- endIndex = startIndex + parseInt($scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(startIndex, endIndex);
+ let query = $scope.search.toLowerCase();
+ // Filter the master list
+ $scope.filteredNodes = nodeStatusCopy.filter(function(node) {
+ return (node.hostname && node.hostname.toLowerCase().includes(query)) ||
+ (node.opstate && node.opstate.toLowerCase().includes(query)) ||
+ (node.comstate && node.comstate.toLowerCase().includes(query)) ||
+ (node.uuid && node.uuid.toLowerCase().includes(query));
+ });
}
- }
+ $scope.totalItems = $scope.filteredNodes.length;
+ $scope.UpdateRecordsToShow(); // Re-calculate pagination
+ };
+ /* If option is 'All' display all records, else display specified records on page */
+ $scope.UpdateRecordsToShow = () => {
+ if ($scope.RecordsToDisplay === 'All') {
+ $scope.lastIndex = 1;
+ $scope.nodeStatus = $scope.filteredNodes;
+ } else {
+ let limit = parseInt($scope.RecordsToDisplay);
+ $scope.lastIndex = Math.ceil($scope.filteredNodes.length / limit);
+ $scope.nodeStatus = $scope.filteredNodes.slice(0, limit);
+ }
+ $scope.currentPage = 1;
Review Comment:
What should the pager do when the search returns no rows? In that case filteredNodes.length is 0, so lastIndex is 0 but currentPage is still reset to 1. That leaves the UI in an invalid state (Page: of 0) and Next stays enabled because it only checks lastIndex == currentPage.
Can we clamp or hide pagination for the empty-results case?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "navinko (via GitHub)" <gi...@apache.org>.
navinko commented on code in PR #10076:
URL: https://github.com/apache/ozone/pull/10076#discussion_r3221782899
##########
hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js:
##########
@@ -218,28 +221,48 @@
$scope.statistics.containers.health.open_without_pipeline = ctrl.scmcontainermanager.OpenContainersWithoutPipeline;
});
- /*if option is 'All' display all records else display specified record on page*/
- $scope.UpdateRecordsToShow = () => {
- if($scope.RecordsToDisplay == 'All') {
- $scope.lastIndex = 1;
- $scope.nodeStatus = nodeStatusCopy;
+ /* Global Search Logic */
+ $scope.applyGlobalSearch = function() {
+ if (!$scope.search || $scope.search.trim() === "") {
+ // Reset to full list if search is empty
+ $scope.filteredNodes = [...nodeStatusCopy];
} else {
- $scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
- }
- $scope.currentPage = 1;
- }
- /* Page Slicing logic */
- $scope.handlePagination = (pageIndex, isDisabled) => {
- if(!isDisabled && $scope.RecordsToDisplay != 'All') {
- pageIndex = parseInt(pageIndex);
- let startIndex = 0, endIndex = 0;
- $scope.currentPage = pageIndex;
- startIndex = ($scope.currentPage - 1) * parseInt($scope.RecordsToDisplay);
- endIndex = startIndex + parseInt($scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(startIndex, endIndex);
+ let query = $scope.search.toLowerCase();
+ // Filter the master list
+ $scope.filteredNodes = nodeStatusCopy.filter(function(node) {
+ return (node.hostname && node.hostname.toLowerCase().includes(query)) ||
+ (node.opstate && node.opstate.toLowerCase().includes(query)) ||
+ (node.comstate && node.comstate.toLowerCase().includes(query)) ||
+ (node.uuid && node.uuid.toLowerCase().includes(query));
+ });
Review Comment:
Thanks @smengcl for reviewing , Definitely suggested approach would be maintenance free and no need to update the search criteria if any columns are being added/removed.
Instead of manually listing out each and every single column name (like hostname, capacity, uuid, etc.), I just updated the search to use Object.values(node). This tells the code to automatically grab every piece of data inside that specific row, convert it to text, and check if it matches the search query.
This also matches the original search behavior and it automatically future-proofs the page so we never have to update this code if new columns are added/removed later!
-> search by any colume for example , i used lastHeartbeat for dn sits on 2nd page
<img width="1104" height="671" alt="1" src="https://github.com/user-attachments/assets/2e84b7be-bdab-43eb-bb7c-c076029c04b8" />
-> global serach by lastHeartbeat
<img width="1112" height="278" alt="2" src="https://github.com/user-attachments/assets/64dfbcea-853d-4fa3-9d4a-4716f41cff17" />
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "navinko (via GitHub)" <gi...@apache.org>.
navinko commented on code in PR #10076:
URL: https://github.com/apache/ozone/pull/10076#discussion_r3221761432
##########
hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js:
##########
@@ -218,28 +221,48 @@
$scope.statistics.containers.health.open_without_pipeline = ctrl.scmcontainermanager.OpenContainersWithoutPipeline;
});
- /*if option is 'All' display all records else display specified record on page*/
- $scope.UpdateRecordsToShow = () => {
- if($scope.RecordsToDisplay == 'All') {
- $scope.lastIndex = 1;
- $scope.nodeStatus = nodeStatusCopy;
+ /* Global Search Logic */
+ $scope.applyGlobalSearch = function() {
+ if (!$scope.search || $scope.search.trim() === "") {
+ // Reset to full list if search is empty
+ $scope.filteredNodes = [...nodeStatusCopy];
} else {
- $scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
- }
- $scope.currentPage = 1;
- }
- /* Page Slicing logic */
- $scope.handlePagination = (pageIndex, isDisabled) => {
- if(!isDisabled && $scope.RecordsToDisplay != 'All') {
- pageIndex = parseInt(pageIndex);
- let startIndex = 0, endIndex = 0;
- $scope.currentPage = pageIndex;
- startIndex = ($scope.currentPage - 1) * parseInt($scope.RecordsToDisplay);
- endIndex = startIndex + parseInt($scope.RecordsToDisplay);
- $scope.nodeStatus = nodeStatusCopy.slice(startIndex, endIndex);
+ let query = $scope.search.toLowerCase();
+ // Filter the master list
+ $scope.filteredNodes = nodeStatusCopy.filter(function(node) {
+ return (node.hostname && node.hostname.toLowerCase().includes(query)) ||
+ (node.opstate && node.opstate.toLowerCase().includes(query)) ||
+ (node.comstate && node.comstate.toLowerCase().includes(query)) ||
+ (node.uuid && node.uuid.toLowerCase().includes(query));
+ });
}
- }
+ $scope.totalItems = $scope.filteredNodes.length;
+ $scope.UpdateRecordsToShow(); // Re-calculate pagination
+ };
+ /* If option is 'All' display all records, else display specified records on page */
+ $scope.UpdateRecordsToShow = () => {
+ if ($scope.RecordsToDisplay === 'All') {
+ $scope.lastIndex = 1;
+ $scope.nodeStatus = $scope.filteredNodes;
+ } else {
+ let limit = parseInt($scope.RecordsToDisplay);
+ $scope.lastIndex = Math.ceil($scope.filteredNodes.length / limit);
+ $scope.nodeStatus = $scope.filteredNodes.slice(0, limit);
+ }
+ $scope.currentPage = 1;
Review Comment:
Good catch! I missed this scenario of empty list .
In the existing code,the pagination logic is implemented in two separate places:
Once for the initial page load
https://github.com/navinko/ozone/blob/master/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js#L166-L167
> $scope.totalItems = nodeStatusCopy.length;
**$scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);**
$scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
and once user clicks for the dropdown updates.
https://github.com/navinko/ozone/blob/master/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm.js#L253-L254
> $scope.UpdateRecordsToShow = () => {
if($scope.RecordsToDisplay == 'All') {
$scope.lastIndex = 1;
$scope.nodeStatus = nodeStatusCopy;
} else {
**$scope.lastIndex = Math.ceil(nodeStatusCopy.length / $scope.RecordsToDisplay);**
$scope.nodeStatus = nodeStatusCopy.slice(0, $scope.RecordsToDisplay);
}
$scope.currentPage = 1;
}
In both places, if nodeStatusCopy.length would be 0, (cluster with empty datanode list ) the lastIndex evaluated to 0. Since currentPage resets to 1, the lastIndex == currentPage check failed and did not disbale next button leaving the UI broken at "Page 1 of 0" with an active Next button.
In order to fix that used math.max(1,0) logic which will return lastIndex to one during both the cases, fresh load with empty datanode list and once user updates the dropdown. I updated the UpdateRecordsToShow() function to use Math.max(1, Math.ceil(...)) so lastIndex is forced to always be at least 1.
Also, resued UpdateRecordsToShow on initial page load and avoided same logic both place. Now, the initial load just calls the fixed UpdateRecordsToShow() function so everything shares the exact same logic.
This way, any empty result (whether from a search or an empty cluster) safely clamps to "Page 1 of 1" and correctly disables the Next button!
<img width="1155" height="218" alt="3" src="https://github.com/user-attachments/assets/f553e313-fdc8-4fa0-95ae-4367583b8d44" />
<img width="1136" height="279" alt="4" src="https://github.com/user-attachments/assets/4ae7b8e2-8edc-4338-9b55-940243ad3ecc" />
<img width="1120" height="217" alt="5" src="https://github.com/user-attachments/assets/8772cebb-9982-46b9-8d54-3210beef6ac3" />
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "navinko (via GitHub)" <gi...@apache.org>.
navinko commented on PR #10076:
URL: https://github.com/apache/ozone/pull/10076#issuecomment-4424691240
Thanks @smengcl for the review.
Tried addressing suggestions , once you get some time please review once .
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org
Re: [PR] HDDS-15007. SCM Web UI search box should filter the entire datanodes set, not just the displayed rows [ozone]
Posted by "navinko (via GitHub)" <gi...@apache.org>.
navinko commented on PR #10076:
URL: https://github.com/apache/ozone/pull/10076#issuecomment-4360053650
Hi @smengcl @jojochuang
Could you please review the changes.
Thanks.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org