Skip to content

Commit 4cc21f6

Browse files
Added filtering logic for all the fields
1 parent 988f0b7 commit 4cc21f6

File tree

4 files changed

+125
-26
lines changed

4 files changed

+125
-26
lines changed

src/gprofiler/backend/models/metrics_models.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,21 @@ def validate_status(cls, v):
206206
class ProfilingHostStatusRequest(BaseModel):
207207
"""Model for profiling host status request parameters"""
208208

209-
service_name: Optional[str] = None
209+
service_name: Optional[List[str]] = None
210210
exact_match: bool = False
211+
hostname: Optional[List[str]] = None
212+
ip_address: Optional[List[str]] = None
213+
profiling_status: Optional[List[str]] = None
214+
command_type: Optional[List[str]] = None
215+
pids: Optional[List[int]] = None
211216

212217

213218
class ProfilingHostStatus(BaseModel):
214219
id: int
215220
service_name: str
216221
hostname: str
217222
ip_address: str
218-
pids: str
223+
pids: List[int]
219224
command_type: str
220225
profiling_status: str
221226
heartbeat_timestamp: datetime

src/gprofiler/backend/routers/metrics_routes.py

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,23 @@ def get_time_interval_value(start_time: datetime, end_time: datetime, interval:
8282

8383

8484
def profiling_host_status_params(
85-
service_name: Optional[str] = Query(None, description="Filter by service name"),
85+
service_name: Optional[List[str]] = Query(None, description="Filter by service name(s)"),
8686
exact_match: bool = Query(False, description="Use exact match for service name (default: false for partial matching)"),
87+
hostname: Optional[List[str]] = Query(None, description="Filter by hostname(s)"),
88+
ip_address: Optional[List[str]] = Query(None, description="Filter by IP address(es)"),
89+
profiling_status: Optional[List[str]] = Query(None, description="Filter by profiling status(es) (e.g., pending, completed, stopped)"),
90+
command_type: Optional[List[str]] = Query(None, description="Filter by command type(s) (e.g., start, stop)"),
91+
pids: Optional[List[int]] = Query(None, description="Filter by PIDs"),
8792
) -> ProfilingHostStatusRequest:
88-
return ProfilingHostStatusRequest(service_name=service_name, exact_match=exact_match)
93+
return ProfilingHostStatusRequest(
94+
service_name=service_name,
95+
exact_match=exact_match,
96+
hostname=hostname,
97+
ip_address=ip_address,
98+
profiling_status=profiling_status,
99+
command_type=command_type,
100+
pids=pids,
101+
)
89102

90103

91104
@router.get("/instance_type_count", response_model=List[InstanceTypeCount])
@@ -647,19 +660,31 @@ def get_profiling_host_status(
647660
profiling_params: ProfilingHostStatusRequest = Depends(profiling_host_status_params),
648661
):
649662
"""
650-
Get profiling host status with optional service name filtering.
663+
Get profiling host status with optional filtering by multiple parameters.
651664
652665
Args:
653-
profiling_params: ProfilingHostStatusRequest object containing service name and exact match flag
666+
profiling_params: ProfilingHostStatusRequest object containing all filter parameters
654667
655668
Returns:
656-
List of host statuses
669+
List of host statuses filtered by the specified criteria
657670
"""
658671
db_manager = DBManager()
659672

660673
# Get hosts - filter by service_name if provided
661674
if profiling_params.service_name:
662-
hosts = db_manager.get_host_heartbeats_by_service(profiling_params.service_name, exact_match=profiling_params.exact_match)
675+
# For multiple service names, we need to get hosts for each service and combine
676+
all_hosts = []
677+
for service_name in profiling_params.service_name:
678+
hosts = db_manager.get_host_heartbeats_by_service(service_name, exact_match=profiling_params.exact_match)
679+
all_hosts.extend(hosts)
680+
# Remove duplicates based on hostname + service_name combination
681+
seen = set()
682+
hosts = []
683+
for host in all_hosts:
684+
key = (host.get("hostname"), host.get("service_name"))
685+
if key not in seen:
686+
seen.add(key)
687+
hosts.append(host)
663688
else:
664689
hosts = db_manager.get_all_host_heartbeats()
665690

@@ -668,24 +693,63 @@ def get_profiling_host_status(
668693
hostname = host.get("hostname")
669694
host_service_name = host.get("service_name")
670695
ip_address = host.get("ip_address")
671-
pids = "All" # Placeholder, update if you have per-host PID info
696+
697+
# Apply hostname filter (check if hostname matches any in the list)
698+
if profiling_params.hostname and not any(filter_hostname.lower() in hostname.lower() for filter_hostname in profiling_params.hostname):
699+
continue
700+
701+
# Apply IP address filter (check if IP matches any in the list)
702+
if profiling_params.ip_address and not any(filter_ip in ip_address for filter_ip in profiling_params.ip_address):
703+
continue
672704

673705
# Get current profiling command for this host/service
674706
command = db_manager.get_current_profiling_command(hostname, host_service_name)
675707
if command:
676708
profiling_status = command.get("status")
677709
command_type = command.get("command_type", "N/A")
710+
# Extract PIDs from command config if available
711+
combined_config = command.get("combined_config", {})
712+
if isinstance(combined_config, str):
713+
try:
714+
combined_config = json.loads(combined_config)
715+
except json.JSONDecodeError:
716+
combined_config = {}
717+
718+
# Try to get PIDs from the command configuration
719+
command_pids = []
720+
if isinstance(combined_config, dict):
721+
pids_in_config = combined_config.get("pids", [])
722+
if isinstance(pids_in_config, list):
723+
# Convert to integers, filtering out non-numeric values
724+
command_pids = [int(pid) for pid in pids_in_config if str(pid).isdigit()]
678725
else:
679726
profiling_status = "stopped"
680727
command_type = "N/A"
728+
command_pids = []
729+
730+
# Apply profiling status filter (check if status matches any in the list)
731+
if profiling_params.profiling_status and not any(filter_status.lower() == profiling_status.lower() for filter_status in profiling_params.profiling_status):
732+
continue
733+
734+
# Apply command type filter (check if command type matches any in the list)
735+
if profiling_params.command_type and not any(filter_cmd_type.lower() == command_type.lower() for filter_cmd_type in profiling_params.command_type):
736+
continue
737+
738+
# Apply PIDs filter (check if any filter PID matches the command PIDs)
739+
if profiling_params.pids and command_pids:
740+
if not any(filter_pid in command_pids for filter_pid in profiling_params.pids):
741+
continue
742+
elif profiling_params.pids and not command_pids:
743+
# If filtering by PIDs but no PIDs in command, skip this host
744+
continue
681745

682746
results.append(
683747
ProfilingHostStatus(
684748
id=host.get("id", 0),
685749
service_name=host_service_name,
686750
hostname=hostname,
687751
ip_address=ip_address,
688-
pids=pids,
752+
pids=command_pids,
689753
command_type=command_type,
690754
profiling_status=profiling_status,
691755
heartbeat_timestamp=host.get("heartbeat_timestamp"),

src/gprofiler/frontend/src/api/urls.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ export const DATA_URLS = {
3939
GET_NODES_AND_CORES_GRAPH_METRICS: `${API_PREFIX}/metrics/graph/nodes_and_cores`,
4040
GET_SAMPLES: `${API_PREFIX}/metrics/samples`,
4141
GET_API_KEY: `${API_PREFIX}/api_key`,
42+
// Profiling endpoints
43+
GET_PROFILING_HOST_STATUS: `${API_PREFIX}/metrics/profiling/host_status`,
44+
POST_PROFILING_REQUEST: `${API_PREFIX}/metrics/profile_request`,
45+
POST_HEARTBEAT: `${API_PREFIX}/metrics/heartbeat`,
46+
POST_COMMAND_COMPLETION: `${API_PREFIX}/metrics/command_completion`,
47+
// Filter endpoints
4248
FILTERS: `${API_PREFIX}${FILETERS_PREFIX}`,
4349
GET_FILTER_OPTIONS_VALUE: (filterType, params) =>
4450
`${API_PREFIX}${FILETERS_PREFIX}/tags/${filterType}?${stringify(params)}`,

src/gprofiler/frontend/src/components/console/ProfilingStatusPage.jsx

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import queryString from 'query-string';
44
import React, { useCallback, useEffect, useState } from 'react';
55
import { useHistory, useLocation } from 'react-router-dom';
66

7+
import { DATA_URLS } from '../../api/urls';
78
import { formatDate, TIME_FORMATS } from '../../utils/datetimesUtils';
89
import MuiTable from '../common/dataDisplay/table/MuiTable';
910
import PageHeader from '../common/layout/PageHeader';
@@ -64,6 +65,27 @@ const ProfilingStatusPage = () => {
6465
}
6566
}, [location.search]);
6667

68+
// Clean up profile-specific URL parameters when entering dynamic profiling
69+
useEffect(() => {
70+
const searchParams = queryString.parse(location.search);
71+
let hasProfileParams = false;
72+
73+
// Check if any profile-specific parameters exist
74+
const profileParams = ['gtab', 'view', 'time', 'startTime', 'endTime', 'filter', 'rt', 'rtms', 'p', 'pm', 'wt', 'wp', 'search', 'fullscreen'];
75+
76+
profileParams.forEach(param => {
77+
if (searchParams[param] !== undefined) {
78+
delete searchParams[param];
79+
hasProfileParams = true;
80+
}
81+
});
82+
83+
// Only update URL if we found and removed profile-specific parameters
84+
if (hasProfileParams) {
85+
history.replace({ search: queryString.stringify(searchParams) });
86+
}
87+
}, []); // Run only once on component mount
88+
6789
// Update URL when filter changes
6890
const updateURL = useCallback(
6991
(serviceName) => {
@@ -81,8 +103,8 @@ const ProfilingStatusPage = () => {
81103
const fetchProfilingStatus = useCallback((serviceName = null) => {
82104
setLoading(true);
83105
const url = serviceName
84-
? `/api/metrics/profiling/host_status?service_name=${encodeURIComponent(serviceName)}`
85-
: '/api/metrics/profiling/host_status';
106+
? `${DATA_URLS.GET_PROFILING_HOST_STATUS}?service_name=${encodeURIComponent(serviceName)}`
107+
: DATA_URLS.GET_PROFILING_HOST_STATUS;
86108

87109
fetch(url)
88110
.then((res) => res.json())
@@ -170,7 +192,7 @@ const ProfilingStatusPage = () => {
170192
submitData.stop_level = 'host';
171193
}
172194

173-
return fetch('/api/metrics/profile_request', {
195+
return fetch(DATA_URLS.POST_PROFILING_REQUEST, {
174196
method: 'POST',
175197
headers: { 'Content-Type': 'application/json' },
176198
body: JSON.stringify(submitData),
@@ -192,8 +214,8 @@ const ProfilingStatusPage = () => {
192214
return (
193215
<>
194216
<PageHeader title='Dynamic Profiling' />
195-
<Box sx={{ p: 3 }}>
196-
<Box sx={{ mb: 2, display: 'flex', gap: 2, alignItems: 'center' }}>
217+
<Box sx={{ p: { xs: 2, sm: 3, md: 4 }, backgroundColor: 'white.main', minHeight: 'calc(100vh - 100px)' }}>
218+
<Box sx={{ mb: 3, display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
197219
<TextField
198220
label='Filter by service name'
199221
variant='outlined'
@@ -234,17 +256,19 @@ const ProfilingStatusPage = () => {
234256
Refresh
235257
</Button>
236258
</Box>
237-
<MuiTable
238-
columns={columns}
239-
data={rows}
240-
isLoading={loading}
241-
pageSize={50}
242-
rowHeight={50}
243-
autoPageSize
244-
checkboxSelection
245-
onSelectionModelChange={setSelectionModel}
246-
selectionModel={selectionModel}
247-
/>
259+
<Box sx={{ '& .MuiDataGrid-root': { border: 'none' } }}>
260+
<MuiTable
261+
columns={columns}
262+
data={rows}
263+
isLoading={loading}
264+
pageSize={50}
265+
rowHeight={50}
266+
autoPageSize
267+
checkboxSelection
268+
onSelectionModelChange={setSelectionModel}
269+
selectionModel={selectionModel}
270+
/>
271+
</Box>
248272
</Box>
249273
</>
250274
);

0 commit comments

Comments
 (0)