@@ -17,19 +17,28 @@ pub fn subcommands() -> Command {
17
17
. help ( "Storage pool name" ) ,
18
18
) ;
19
19
20
+ let nexus = Command :: new ( "nexus" ) . about ( "Get Nexus IO Stats" ) . arg (
21
+ Arg :: new ( "name" )
22
+ . required ( false )
23
+ . index ( 1 )
24
+ . help ( "Volume target/nexus name" ) ,
25
+ ) ;
26
+
20
27
let reset = Command :: new ( "reset" ) . about ( "Reset all resource IO Stats" ) ;
21
28
22
29
Command :: new ( "stats" )
23
30
. subcommand_required ( true )
24
31
. arg_required_else_help ( true )
25
32
. about ( "Resource IOStats" )
26
33
. subcommand ( pool)
34
+ . subcommand ( nexus)
27
35
. subcommand ( reset)
28
36
}
29
37
30
38
pub async fn handler ( ctx : Context , matches : & ArgMatches ) -> crate :: Result < ( ) > {
31
39
match matches. subcommand ( ) . unwrap ( ) {
32
40
( "pool" , args) => pool ( ctx, args) . await ,
41
+ ( "nexus" , args) => nexus ( ctx, args) . await ,
33
42
( "reset" , _) => reset ( ctx) . await ,
34
43
( cmd, _) => {
35
44
Err ( Status :: not_found ( format ! ( "command {cmd} does not exist" ) ) )
@@ -38,13 +47,108 @@ pub async fn handler(ctx: Context, matches: &ArgMatches) -> crate::Result<()> {
38
47
}
39
48
}
40
49
41
- async fn pool ( mut ctx : Context , _matches : & ArgMatches ) -> crate :: Result < ( ) > {
50
+ async fn pool ( mut ctx : Context , matches : & ArgMatches ) -> crate :: Result < ( ) > {
42
51
ctx. v2 ( "Requesting Pool metrics" ) ;
52
+ let pool_name = matches. get_one :: < String > ( "name" ) ;
43
53
let response = ctx
44
54
. v1
45
55
. stats
46
56
. get_pool_io_stats ( v1rpc:: stats:: ListStatsOption {
47
- name : None ,
57
+ name : pool_name. cloned ( ) ,
58
+ } )
59
+ . await
60
+ . context ( GrpcStatus ) ?;
61
+ match ctx. output {
62
+ OutputFormat :: Json => {
63
+ println ! (
64
+ "{}" ,
65
+ serde_json:: to_string_pretty( response. get_ref( ) )
66
+ . unwrap( )
67
+ . to_colored_json_auto( )
68
+ . unwrap( )
69
+ ) ;
70
+ }
71
+ OutputFormat :: Default => {
72
+ let stats: & Vec < v1rpc:: stats:: IoStats > = & response. get_ref ( ) . stats ;
73
+ if stats. is_empty ( ) {
74
+ if let Some ( name) = pool_name {
75
+ ctx. v1 ( & format ! (
76
+ "No IoStats found for {}, Check if device exist" ,
77
+ name
78
+ ) ) ;
79
+ } else {
80
+ ctx. v1 ( "No Pool IoStats found" ) ;
81
+ }
82
+ return Ok ( ( ) ) ;
83
+ }
84
+
85
+ let table = stats
86
+ . iter ( )
87
+ . map ( |p| {
88
+ let read_latency =
89
+ ticks_to_time ( p. read_latency_ticks , p. tick_rate ) ;
90
+ let write_latency =
91
+ ticks_to_time ( p. write_latency_ticks , p. tick_rate ) ;
92
+ let unmap_latency =
93
+ ticks_to_time ( p. unmap_latency_ticks , p. tick_rate ) ;
94
+ let max_read_latency =
95
+ ticks_to_time ( p. max_read_latency_ticks , p. tick_rate ) ;
96
+ let min_read_latency =
97
+ ticks_to_time ( p. min_read_latency_ticks , p. tick_rate ) ;
98
+ let max_write_latency =
99
+ ticks_to_time ( p. max_write_latency_ticks , p. tick_rate ) ;
100
+ let min_write_latency =
101
+ ticks_to_time ( p. min_write_latency_ticks , p. tick_rate ) ;
102
+ vec ! [
103
+ p. name. clone( ) ,
104
+ p. num_read_ops. to_string( ) ,
105
+ adjust_bytes( p. bytes_read) ,
106
+ p. num_write_ops. to_string( ) ,
107
+ adjust_bytes( p. bytes_written) ,
108
+ p. num_unmap_ops. to_string( ) ,
109
+ adjust_bytes( p. bytes_unmapped) ,
110
+ read_latency. to_string( ) ,
111
+ write_latency. to_string( ) ,
112
+ unmap_latency. to_string( ) ,
113
+ max_read_latency. to_string( ) ,
114
+ min_read_latency. to_string( ) ,
115
+ max_write_latency. to_string( ) ,
116
+ min_write_latency. to_string( ) ,
117
+ ]
118
+ } )
119
+ . collect ( ) ;
120
+ ctx. print_list (
121
+ vec ! [
122
+ "NAME" ,
123
+ "NUM_RD_OPS" ,
124
+ "TOTAL_RD" ,
125
+ "NUM_WR_OPS" ,
126
+ "TOTAL_WR" ,
127
+ "NUM_UNMAP_OPS" ,
128
+ "TOTAL_UNMAPPED" ,
129
+ "RD_LAT" ,
130
+ "WR_LAT" ,
131
+ "UNMAP_LATENCY" ,
132
+ "MAX_RD_LAT" ,
133
+ "MIN_RD_LAT" ,
134
+ "MAX_WR_LAT" ,
135
+ "MIN_WR_LAT" ,
136
+ ] ,
137
+ table,
138
+ ) ;
139
+ }
140
+ } ;
141
+ Ok ( ( ) )
142
+ }
143
+
144
+ async fn nexus ( mut ctx : Context , matches : & ArgMatches ) -> crate :: Result < ( ) > {
145
+ ctx. v2 ( "Requesting Nexus metrics" ) ;
146
+ let nexus_name = matches. get_one :: < String > ( "name" ) ;
147
+ let response = ctx
148
+ . v1
149
+ . stats
150
+ . get_nexus_io_stats ( v1rpc:: stats:: ListStatsOption {
151
+ name : nexus_name. cloned ( ) ,
48
152
} )
49
153
. await
50
154
. context ( GrpcStatus ) ?;
@@ -61,7 +165,14 @@ async fn pool(mut ctx: Context, _matches: &ArgMatches) -> crate::Result<()> {
61
165
OutputFormat :: Default => {
62
166
let stats: & Vec < v1rpc:: stats:: IoStats > = & response. get_ref ( ) . stats ;
63
167
if stats. is_empty ( ) {
64
- ctx. v1 ( "No Pool IoStats found" ) ;
168
+ if let Some ( name) = nexus_name {
169
+ ctx. v1 ( & format ! (
170
+ "No IoStats found for {}, Check if device exists" ,
171
+ name
172
+ ) ) ;
173
+ } else {
174
+ ctx. v1 ( "No Nexus IoStats found" ) ;
175
+ }
65
176
return Ok ( ( ) ) ;
66
177
}
67
178
0 commit comments