1
1
//! `mmlsdisk` parsing.
2
2
3
- use std:: io:: BufRead ;
3
+ use std:: collections:: HashMap ;
4
+ use std:: fmt:: Display ;
5
+ use std:: io:: { BufRead , Write } ;
4
6
use std:: process:: Command ;
7
+ use std:: str:: FromStr ;
5
8
6
9
use anyhow:: { anyhow, Context , Result } ;
7
10
@@ -12,7 +15,7 @@ use crate::util::MMBool;
12
15
/// # Errors
13
16
///
14
17
/// Returns an error if running `mmlsdisk` fails or if parsing its output fails.
15
- pub fn disks < S : AsRef < str > > ( fs_name : S ) -> Result < Disks > {
18
+ pub fn disks ( fs_name : impl AsRef < str > ) -> Result < Disks > {
16
19
let fs_name = fs_name. as_ref ( ) ;
17
20
18
21
let mut cmd = Command :: new ( "mmlsdisk" ) ;
@@ -102,12 +105,61 @@ impl FromIterator<Self> for Disks {
102
105
}
103
106
}
104
107
108
+ /// Disk availability.
109
+ #[ derive( Clone , Eq , PartialEq , Ord , PartialOrd , Hash , Debug ) ]
110
+ #[ non_exhaustive]
111
+ pub enum Availability {
112
+ /// Disk is available for I/O operations.
113
+ Up ,
114
+
115
+ /// No I/O operations can be performed.
116
+ Down ,
117
+
118
+ /// Intermediate state for disks coming up.
119
+ Recovering ,
120
+
121
+ /// Disk was not successfully brought up.
122
+ Unrecovered ,
123
+
124
+ /// Unknown state.
125
+ Unknown ( String ) ,
126
+ }
127
+
128
+ impl Display for Availability {
129
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
130
+ let s = match self {
131
+ Self :: Up => "up" ,
132
+ Self :: Down => "down" ,
133
+ Self :: Recovering => "recovering" ,
134
+ Self :: Unrecovered => "unrecovered" ,
135
+ Self :: Unknown ( s) => s. as_str ( ) ,
136
+ } ;
137
+
138
+ write ! ( f, "{s}" )
139
+ }
140
+ }
141
+
142
+ impl FromStr for Availability {
143
+ type Err = anyhow:: Error ;
144
+
145
+ fn from_str ( s : & str ) -> Result < Self > {
146
+ match s {
147
+ "up" => Ok ( Self :: Up ) ,
148
+ "down" => Ok ( Self :: Down ) ,
149
+ "recovering" => Ok ( Self :: Recovering ) ,
150
+ "unrecovered" => Ok ( Self :: Unrecovered ) ,
151
+ unknown => Ok ( Self :: Unknown ( unknown. into ( ) ) ) ,
152
+ }
153
+ }
154
+ }
155
+
105
156
/// Disk data.
106
157
#[ derive( Clone , Eq , PartialEq , Ord , PartialOrd , Hash , Debug ) ]
107
158
pub struct Disk {
108
159
nsd_name : String ,
109
160
is_metadata : bool ,
110
161
is_objectdata : bool ,
162
+ availability : Availability ,
111
163
storage_pool : String ,
112
164
}
113
165
@@ -159,6 +211,12 @@ impl Disk {
159
211
let is_objectdata =
160
212
tokens[ is_objectdata_index] . parse :: < MMBool > ( ) ?. as_bool ( ) ;
161
213
214
+ let availability_index = index
215
+ . availability
216
+ . ok_or_else ( || anyhow ! ( "no availability index" ) ) ?;
217
+ let availability =
218
+ tokens[ availability_index] . parse :: < Availability > ( ) ?;
219
+
162
220
let storage_pool_index = index
163
221
. storage_pool
164
222
. ok_or_else ( || anyhow ! ( "no storage pool index" ) ) ?;
@@ -168,6 +226,7 @@ impl Disk {
168
226
nsd_name,
169
227
is_metadata,
170
228
is_objectdata,
229
+ availability,
171
230
storage_pool,
172
231
} )
173
232
}
@@ -178,6 +237,7 @@ struct Index {
178
237
nsd_name : Option < usize > ,
179
238
is_metadata : Option < usize > ,
180
239
is_objectdata : Option < usize > ,
240
+ availability : Option < usize > ,
181
241
storage_pool : Option < usize > ,
182
242
}
183
243
@@ -187,57 +247,128 @@ fn header_to_index(tokens: &[&str], index: &mut Index) {
187
247
"nsdName" => index. nsd_name = Some ( i) ,
188
248
"metadata" => index. is_metadata = Some ( i) ,
189
249
"data" => index. is_objectdata = Some ( i) ,
250
+ "availability" => index. availability = Some ( i) ,
190
251
"storagePool" => index. storage_pool = Some ( i) ,
191
252
_ => { }
192
253
}
193
254
}
194
255
}
195
256
257
+ // ----------------------------------------------------------------------------
258
+ // prometheus
259
+ // ----------------------------------------------------------------------------
260
+
261
+ impl < S : :: std:: hash:: BuildHasher > crate :: prom:: ToText
262
+ for HashMap < String , Disks , S >
263
+ {
264
+ fn to_prom ( & self , output : & mut impl Write ) -> Result < ( ) > {
265
+ for ( fs, disks) in self {
266
+ writeln ! (
267
+ output,
268
+ "# HELP gpfs_disk_availability GPFS disk state."
269
+ ) ?;
270
+ writeln ! ( output, "# TYPE gpfs_disk_availability gauge" ) ?;
271
+
272
+ for disk in & disks. 0 {
273
+ let status = match disk. availability {
274
+ Availability :: Up => 0 ,
275
+ _ => 1 ,
276
+ } ;
277
+
278
+ writeln ! (
279
+ output,
280
+ "gpfs_disk_availability{{name=\" {}\" ,fs=\" {}\" ,pool=\" {}\" ,availability=\" {}\" }} {}" ,
281
+ disk. nsd_name,
282
+ fs,
283
+ disk. storage_pool,
284
+ disk. availability,
285
+ status,
286
+ ) ?;
287
+ }
288
+ }
289
+
290
+ Ok ( ( ) )
291
+ }
292
+ }
293
+
196
294
// ----------------------------------------------------------------------------
197
295
// tests
198
296
// ----------------------------------------------------------------------------
199
297
200
298
#[ cfg( test) ]
201
299
mod tests {
202
300
use super :: * ;
301
+ use crate :: prom:: ToText ;
203
302
204
303
#[ test]
205
304
fn parse ( ) {
206
305
let input = include_str ! ( "disk-example.in" ) ;
207
306
208
- let fs = Disks :: from_reader ( input. as_bytes ( ) ) . unwrap ( ) ;
209
- let mut fs = fs . 0 . into_iter ( ) ;
307
+ let disks = Disks :: from_reader ( input. as_bytes ( ) ) . unwrap ( ) ;
308
+ let mut disks = disks . 0 . into_iter ( ) ;
210
309
211
310
assert_eq ! (
212
- fs . next( ) ,
311
+ disks . next( ) ,
213
312
Some ( Disk {
214
313
nsd_name: "disk1" . into( ) ,
215
314
is_metadata: true ,
216
315
is_objectdata: false ,
316
+ availability: Availability :: Up ,
217
317
storage_pool: "system" . into( ) ,
218
318
} )
219
319
) ;
220
320
221
321
assert_eq ! (
222
- fs . next( ) ,
322
+ disks . next( ) ,
223
323
Some ( Disk {
224
324
nsd_name: "disk2" . into( ) ,
225
325
is_metadata: false ,
226
326
is_objectdata: true ,
327
+ availability: Availability :: Down ,
227
328
storage_pool: "nvme" . into( ) ,
228
329
} )
229
330
) ;
230
331
231
332
assert_eq ! (
232
- fs . next( ) ,
333
+ disks . next( ) ,
233
334
Some ( Disk {
234
335
nsd_name: "disk3" . into( ) ,
235
336
is_metadata: false ,
236
337
is_objectdata: true ,
338
+ availability: Availability :: Recovering ,
237
339
storage_pool: "nlsas" . into( ) ,
238
340
} )
239
341
) ;
240
342
241
- assert_eq ! ( fs. next( ) , None ) ;
343
+ assert_eq ! (
344
+ disks. next( ) ,
345
+ Some ( Disk {
346
+ nsd_name: "disk4" . into( ) ,
347
+ is_metadata: false ,
348
+ is_objectdata: true ,
349
+ availability: Availability :: Unrecovered ,
350
+ storage_pool: "nlsas" . into( ) ,
351
+ } )
352
+ ) ;
353
+
354
+ assert_eq ! ( disks. next( ) , None ) ;
355
+ }
356
+
357
+ #[ test]
358
+ fn prometheus ( ) {
359
+ let input = include_str ! ( "disk-example.in" ) ;
360
+
361
+ let disks = Disks :: from_reader ( input. as_bytes ( ) ) . unwrap ( ) ;
362
+
363
+ let mut all_disks = HashMap :: new ( ) ;
364
+ all_disks. insert ( String :: from ( "gpfs1" ) , disks) ;
365
+
366
+ let mut output = vec ! [ ] ;
367
+ all_disks. to_prom ( & mut output) . unwrap ( ) ;
368
+
369
+ let metrics = std:: str:: from_utf8 ( output. as_slice ( ) ) . unwrap ( ) ;
370
+
371
+ let expected = include_str ! ( "disk-example.prom" ) ;
372
+ assert_eq ! ( metrics, expected) ;
242
373
}
243
374
}
0 commit comments