@@ -13,27 +13,63 @@ class NodeStats:
13
13
"""
14
14
15
15
@staticmethod
16
- def cpu_stats (node_obj ):
16
+ def cpu_stats (node_obj , interval = 1 , count = 2 , format = "json" ):
17
17
"""
18
18
Get CPU statistics for a given node using `mpstat`.
19
19
20
20
Args:
21
- node_obj (OCSNode): Node object to query.
21
+ node_obj (OCSNode): The node object to fetch stats from.
22
+ interval (int): Interval in seconds between samples. Default 1.
23
+ count (int): Number of samples to take. Default 2.
24
+ format (str): Output format - "json" or "text". Default "json".
22
25
23
26
Returns:
24
- list: List of CPU stats dictionaries parsed from mpstat JSON output.
27
+ list or str or None: Parsed statistics list (JSON), raw output (text),
28
+ or None on failure.
25
29
"""
26
- ocp_obj = ocp .OCP (kind = "node" )
27
- cmd = f"debug nodes/{ node_obj .name } -- mpstat 1 2 -o JSON"
30
+ log .info (
31
+ f"Running mpstat on node '{ node_obj .name } ' with interval={ interval } , count={ count } , format={ format } "
32
+ )
33
+
34
+ if format not in ("json" , "text" ):
35
+ log .error (f"Unsupported format '{ format } '. Use 'json' or 'text'." )
36
+ return None
28
37
38
+ cmd = f"mpstat { interval } { count } "
39
+ if format == "json" :
40
+ cmd += " -o JSON"
41
+ elif format == "text" :
42
+ cmd += " > /tmp/mpstat.txt && cat /tmp/mpstat.txt"
43
+ log .warning ("Text format selected. Output will be raw and unparsed." )
44
+
45
+ ocp_obj = ocp .OCP (kind = "node" )
29
46
try :
30
- log .info (f"Running mpstat on node: { node_obj .name } " )
31
- cmd_output = ocp_obj .exec_oc_cmd (command = cmd , out_yaml_format = False )
32
- output = json .loads (cmd_output )
33
- return output .get ("sysstat" , {}).get ("hosts" , [{}])[0 ].get ("statistics" , [])
47
+ cmd_output = ocp_obj .exec_oc_debug_cmd (
48
+ node = node_obj .name , cmd_list = [cmd ], use_root = False
49
+ )
50
+
51
+ if not cmd_output :
52
+ log .warning ("Empty response received from mpstat command" )
53
+ return None
54
+
55
+ if format == "json" :
56
+ try :
57
+ output = json .loads (cmd_output )
58
+ return (
59
+ output .get ("sysstat" , {})
60
+ .get ("hosts" , [{}])[0 ]
61
+ .get ("statistics" , [])
62
+ )
63
+ except json .JSONDecodeError as e :
64
+ log .error (
65
+ f"Failed to parse JSON from mpstat on node '{ node_obj .name } ': { e } "
66
+ )
67
+ return None
68
+ return cmd_output .splitlines ()
69
+
34
70
except CommandFailed as e :
35
- log .error (f"Failed to fetch CPU stats for node { node_obj .name } : { e } " )
36
- return []
71
+ log .error (f"Failed to fetch CPU stats from node ' { node_obj .name } ' : { e } " )
72
+ return None
37
73
38
74
@staticmethod
39
75
def memory_usage_percent (node_obj ):
@@ -47,15 +83,20 @@ def memory_usage_percent(node_obj):
47
83
float: Used memory percentage.
48
84
"""
49
85
ocp_obj = ocp .OCP (kind = "node" )
50
- cmd = f"debug nodes/ { node_obj . name } -- cat /proc/meminfo"
86
+ cmd = " cat /proc/meminfo"
51
87
52
88
try :
53
- output = ocp_obj .exec_oc_cmd (command = cmd , out_yaml_format = False )
89
+ output = ocp_obj .exec_oc_debug_cmd (
90
+ node = node_obj .name , cmd_list = [cmd ], use_root = False , timeout = 30
91
+ )
54
92
55
93
meminfo = {}
56
94
for line in output .splitlines ():
57
- key , value = line .strip ().split (":" , 1 )
58
- meminfo [key ] = int (value .strip ().split ()[0 ]) # in KB
95
+ try :
96
+ key , value = line .strip ().split (":" , 1 )
97
+ meminfo [key ] = int (value .strip ().split ()[0 ]) # in KB
98
+ except (ValueError , IndexError ):
99
+ continue
59
100
60
101
mem_total = meminfo .get ("MemTotal" )
61
102
mem_available = meminfo .get ("MemAvailable" )
@@ -64,57 +105,88 @@ def memory_usage_percent(node_obj):
64
105
used_percent = ((mem_total - mem_available ) / mem_total ) * 100
65
106
log .info (f"Memory usage on node { node_obj .name } : { used_percent :.2f} %" )
66
107
return round (used_percent , 2 )
67
- else :
68
- log .warning ("Missing MemTotal or MemAvailable in /proc/meminfo" )
69
- return 0.0
108
+
109
+ log .warning ("Missing MemTotal or MemAvailable in /proc/meminfo" )
110
+ return 0.0
70
111
71
112
except CommandFailed as e :
72
- log .error (f"Failed to compute memory usage on node { node_obj .name } : { e } " )
113
+ log .error (
114
+ f"Failed to compute memory usage on node { node_obj .name } : { str (e )} "
115
+ )
73
116
return 0.0
74
117
75
118
@staticmethod
76
- def disk_stats (node_obj ):
119
+ def disk_stats (node_obj , format = "json" , interval = 1 , count = 2 ):
77
120
"""
78
121
Get disk I/O statistics using `iostat`.
79
122
80
123
Args:
81
124
node_obj (OCSNode): Node object to query.
125
+ format (str): Output format ("json" or "text"). Default "json".
126
+ interval (int): Interval in seconds between samples. Default 1.
127
+ count (int): Number of samples to take. Default 2.
82
128
83
129
Returns:
84
- dict: Latest disk statistics.
130
+ dict or list : Latest disk statistics (dict for JSON, list for text) .
85
131
"""
86
132
ocp_obj = ocp .OCP (kind = "node" )
87
- cmd = f"debug nodes/{ node_obj .name } -- iostat -xt -o JSON 1 2"
133
+ if format not in ("json" , "text" ):
134
+ log .error (f"Unsupported format '{ format } '. Use 'json' or 'text'." )
135
+ return {}
136
+
137
+ cmd = f"iostat -xt { interval } { count } "
138
+ if format == "json" :
139
+ cmd += " -o JSON"
88
140
89
141
try :
90
- log .info (f"Running disk stats command on node: { node_obj .name } " )
91
- cmd_output = ocp_obj .exec_oc_cmd (command = cmd , out_yaml_format = False )
92
- output = json .loads (cmd_output )
93
- stats = (
94
- output .get ("sysstat" , {}).get ("hosts" , [{}])[0 ].get ("statistics" , [])
142
+ output = ocp_obj .exec_oc_debug_cmd (
143
+ node = node_obj .name , cmd_list = [cmd ], use_root = False , timeout = 30
95
144
)
96
- return stats [- 1 ] if stats else {}
145
+
146
+ if format == "json" :
147
+ try :
148
+ output = json .loads (output )
149
+ stats = (
150
+ output .get ("sysstat" , {})
151
+ .get ("hosts" , [{}])[0 ]
152
+ .get ("statistics" , [])
153
+ )
154
+ return stats [- 1 ] if stats else {}
155
+ except json .JSONDecodeError as e :
156
+ log .error (
157
+ f"Failed to parse JSON from iostat on node '{ node_obj .name } ': { e } "
158
+ )
159
+ return {}
160
+
161
+ return output .splitlines ()
162
+
97
163
except CommandFailed as e :
98
- log .error (f"Failed to fetch disk stats for node { node_obj .name } : { e } " )
164
+ log .error (f"Failed to fetch disk stats from node ' { node_obj .name } ' : { e } " )
99
165
return {}
100
166
101
167
@staticmethod
102
- def network_stats (node_obj , interface = "ovn-k8s-mp0" ):
168
+ def network_stats (node_obj , interface = "ovn-k8s-mp0" , interval = 1 , count = 2 ):
103
169
"""
104
170
Get network interface statistics using `sar`.
105
171
106
172
Args:
107
173
node_obj (OCSNode): Node object to query.
174
+ interface (str): Network interface to monitor. Default "ovn-k8s-mp0".
175
+ interval (int): Interval in seconds between samples. Default 1.
176
+ count (int): Number of samples to take. Default 2.
108
177
109
178
Returns:
110
- str : Network interface statistics as text.
179
+ list : Network interface statistics as text lines .
111
180
"""
112
181
ocp_obj = ocp .OCP (kind = "node" )
113
- cmd = f"debug nodes/ { node_obj . name } -- sar -n DEV 1 1 "
182
+ cmd = f"sar -n DEV { interval } { count } "
114
183
115
184
try :
185
+ output = ocp_obj .exec_oc_debug_cmd (
186
+ node = node_obj .name , cmd_list = [cmd ], use_root = False
187
+ )
116
188
log .info (f"Running network stats command on node: { node_obj .name } " )
117
- return ocp_obj . exec_oc_cmd ( command = cmd , out_yaml_format = False )
189
+ return output . splitlines ( )
118
190
except CommandFailed as e :
119
191
log .error (f"Failed to fetch network stats for node { node_obj .name } : { e } " )
120
- return ""
192
+ return []
0 commit comments