cephDiskPerf.pl 8.92 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/perl
################################################################################
# cephDiskPerf.pl - Execute 'ceph osd perf' and return one in:
#     - osdperf.{all|this}.{clat|clatdev|alat|alatdev}: 
#   where:
#        all: is for all OSDs in cluster
#        this: only for OSDs attached to current host
#   and:
#        clat: commit latency average (ms)
#        clatdev: commit latency average (ms)
#        alat: commit latency average (ms)
#        alatdev: apply latency standard deviation (ms)
#
#   for 'this' values, returned disk list is checked against
#      systemctl list-units --all | grep '^\s*ceph-osd\S*service'
#
# Information to be returned is selected via switches:
#    -i clat|alat
#    -w dev   (optional argument, assume 'avg' when missing)
#
################################################################################
use strict;
use warnings;

use Getopt::Long;
use Math::Complex;
27
use List::Util qw( min max );
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

sub fail_usage
{
    my ($msg)=@_;
    print STDERR $msg."\n" if $msg;
    print STDERR "Please use '-h' for usage.\n";
    exit 1;
}


my $_sudo = `which sudo`;
chomp $_sudo;

### Options
our($opt_exec, $opt_exec2, $opt_become, $opt_cluster, $opt_user, $opt_keyring, $opt_monhost, 
43
    $opt_thishost, $opt_iwant1, $opt_iwant2, $opt_sizeOrDev, $opt_select, $opt_range, $opt_debug, $opt_h);
44
45
46
47
48
49
50
51
52
53
54
if (@ARGV > 0) {
    GetOptions("e=s"=>\$opt_exec,
	       "l=s"=>\$opt_exec2,
               "b"  =>\$opt_become,
               "c=s"=>\$opt_cluster,
               "u=s"=>\$opt_user,
               "k=s"=>\$opt_keyring,
               "m=s"=>\$opt_monhost,
               "t"  =>\$opt_thishost,
	       "i=s"=>\$opt_iwant1,
	       "w=s"=>\$opt_iwant2,
55
56
               "p"  =>\$opt_sizeOrDev,
               "s=s"=>\$opt_select,
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
               "r=f"=>\$opt_range,
               "d"  =>\$opt_debug,
               "h"  =>\$opt_h) || fail_usage;
} fail_usage "Unknown parameter." if (@ARGV > 0);

my $cephCmd = "/usr/bin/ceph";
if (defined $opt_exec) {
    $cephCmd = "$opt_exec";
}
if ( ! -e $cephCmd) {
    die "Executable $cephCmd not found!";
}
my $systemctlCmd = 'systemctl list-units --all | grep \'^\s*ceph-osd\\S*service\'';
if (defined $opt_exec2) {
    $systemctlCmd = "$opt_exec2";
}
if (defined $opt_become) {
    $cephCmd = "$_sudo $cephCmd";
}
#
if (defined $opt_cluster) {
    $cephCmd .= " --cluster $opt_cluster";
}
if (defined $opt_user) {
    $cephCmd .= " --user $opt_user";
}
if (defined $opt_keyring) {
    $cephCmd .= " --keyring $opt_keyring";
}
if (defined $opt_monhost) {
    $cephCmd .= " -m $opt_monhost";
}
#
90
91
my $perfType = "clat";
my $fracType = "";
92
93
my $perfWhat = 'avg';
if (defined $opt_iwant1) {
94
95
96
97
98
99
100
101
    fail_usage "Info option -i expects either 'clat'|'alat' or 'minfrac'|'maxfrac' only."
	unless ($opt_iwant1 eq 'alat' || $opt_iwant1 eq 'clat' || $opt_iwant1 eq 'maxfrac' || $opt_iwant1 eq 'minfrac');
    if ( $opt_iwant1 eq 'alat' || $opt_iwant1 eq 'clat' ) {
	$perfType = $opt_iwant1;
    }
    if ( $opt_iwant1 eq 'maxfrac' || $opt_iwant1 eq 'minfrac' ) {
	$fracType = $opt_iwant1;
    }
102
103
104
}
if (defined $opt_iwant2) {
    $perfWhat = $opt_iwant2;
105
106
    fail_usage "Info option -w currently expects 'dev' only, in conjunction with -i 'clat'|'alat'"
	unless ($perfWhat eq 'dev' and ($perfType eq 'alat' || $perfType eq 'clat'));
107
108
109
110
111
112
}
#
my $thisHost = 0;
if (defined $opt_thishost) {
    $thisHost = 1;
}
113
114
115
116
117
118
# 0 is default, group by device class
# 1 group by disk size
my $selectSizeOrDev = 0;
if (defined $opt_sizeOrDev) {
    $selectSizeOrDev = 1;
}
119
120
121
my $selectSize = 0.;
if (defined $opt_select) {
    $selectSize = $opt_select;
122
123
124
    if ($selectSizeOrDev) {
	$selectSize += 0.;
    }
125
}
126
127
if ($selectSizeOrDev > 0 and $selectSize < 0.) {
    fail_usage "SelectSize option -s expects positive float, when used together with -p option.";
128
}
129
my $range = .1;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
if (defined $opt_range) {
    $range = $opt_range;
}
unless ($range >= 0.) {
    fail_usage "Range option -r expects positive float.";
}

# Possibly fetch list of OSDs on this host
my @_systemctl = ();
my %osdThisHost = ();
if ($thisHost) {
    @_systemctl = `$systemctlCmd`;
    foreach my $_sysLine (@_systemctl) {
	$_sysLine =~ s/^\s+//;
	$_sysLine =~ s/\s+$//;
	my @fields = split(/\s+/, $_sysLine);
	next unless ($fields[3] =~ m/running/);
	if ($fields[0] =~ /@(\d+)\.service/) {
	    $osdThisHost{$1} = 1.;
	}
    }
    my $osdList = "OSD this host:";
    foreach (sort {$a<=>$b} keys %osdThisHost) {
	$osdList .= " ".$_;
    }
    print $osdList."\n" if (defined $opt_debug);
}

# Fetch the data from 'ceph osd df' and put it in an hash of arrays indexed by size in GB
my @_dataSize = `$cephCmd osd df`;
chomp @_dataSize;
my %dataSizeH = ();
162
my %dataTypeH = ();
163
164
my %fracSizeH = ();
my %fracTypeH = ();
165
166
167
168
169
170
171
172
foreach my $_lineS (@_dataSize)
{
    # Removing leading and trailing blanks
    $_lineS =~ s/^\s+//;
    $_lineS =~ s/\s+$//;
    my @fields = split(/\s+/, $_lineS);
    next unless ($fields[0] =~ /^\d+$/);
    my $osdNum = $fields[0];
173
    my $type = $fields[1];
174
    my $frac = $fields[11];
175
    my $size = $fields[4];
176
    $size =~ s/iB$//;
177
    $size =~ s/B$//;
178
179
180
    my $sizeLastChar = lc(chop($size));
    if ($sizeLastChar eq "t") {
	$size = $size * 1000.;
181
182
183
184
    } elsif ($sizeLastChar eq "g") {
      $size += 0.;
    } elsif ($size eq "") {
      $size = 0.;
185
    }
186
187
    $size += 0.;

188
    push (@{$dataSizeH{$size}}, $osdNum);
189
    push (@{$dataTypeH{$type}}, $osdNum);
190
191
    push (@{$fracSizeH{$size}}, $frac);
    push (@{$fracTypeH{$type}}, $frac);
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
}

# Fetch the data from 'ceph osd perf' and put it in an hash indexed by osd#
my @_data = `$cephCmd osd perf`;
chomp @_data;
my %dataHash = ();
my %finalHash = ();
# Read the array and print the wanted data
foreach my $_line (@_data)
{
    # Removing leading and trailing blanks
    $_line =~ s/^\s+//;
    $_line =~ s/\s+$//;
    my @fields = split(/\s+/, $_line);
    next unless ($fields[0] =~ /^\d+$/);
    my $osdNum = $fields[0];
    $dataHash{$osdNum}{'clat'} = $fields[1];
    $dataHash{$osdNum}{'alat'} = $fields[2];
}
foreach (sort {$a<=> $b} keys %dataHash) {
    print "Line  accepted: ".$_." ".$dataHash{$_}{'clat'}." ".$dataHash{$_}{'alat'}."\n" if (defined $opt_debug);
}

my $lastKey = 0.;
my $selectMin = 0.;
my $selectMax = (sort {$b<=>$a} keys %{dataSizeH})[0] * 1.1;
218
219
220
221
222
223
224
if ($selectSizeOrDev) {
    if ($selectSize > 0) {
	$lastKey = $selectSize;
	$selectMin = $selectSize * (1. - ($range/100.));
	$selectMin = 0 if ($selectMin < 0.);
	$selectMax = $selectSize * (1. + ($range/100.));
    }
225
226
227
228
229
}
print "Select: $selectMin - $selectSize - $selectMax\n" if (defined $opt_debug);

my @latArray = ();
my $count = 0;
230
231
my $minFrac = 100.;
my $maxFrac = 0.;
232
233
234
235
236
237
if ($selectSizeOrDev) {
    foreach my $aKey (sort {$a<=>$b} keys %dataSizeH) {
	next unless $aKey >= $selectMin;
	next if $aKey >= $selectMax;
	foreach my $elem (sort {$a<=> $b} @{$dataSizeH{$aKey}}) {
	    next if ($thisHost && not exists $osdThisHost{$elem});
238
	    next if (not defined $dataHash{$elem}{$perfType});
239
	    print "PerfType;ush: OSD=$elem size=$aKey lat=".$dataHash{$elem}{$perfType}."\n" if (defined $opt_debug);
240
241
242
	    push (@latArray, $dataHash{$elem}{$perfType});
	    $count++;
	}
243
244
245
246
247
248
	my $aMinFrac = min( @{$fracSizeH{$aKey}});
	my $aMaxFrac = max( @{$fracSizeH{$aKey}});
	print "    min/max frac: ".$aMinFrac."/".$aMaxFrac."\n" if (defined $opt_debug);
	$minFrac = $aMinFrac if ( $aMinFrac < $minFrac );
	$maxFrac = $aMaxFrac if ( $aMaxFrac > $maxFrac );
	print "New min/max frac: ".$aMinFrac."/".$aMaxFrac."\n" if (defined $opt_debug);
249
250
251
252
253
254
    }
} else {
    foreach my $aKey (sort keys %dataTypeH) {
	next unless $aKey =~ $selectSize;
	foreach my $elem (sort {$a<=> $b} @{$dataTypeH{$aKey}}) {
	    next if ($thisHost && not exists $osdThisHost{$elem});
255
	    next if (not defined $dataHash{$elem}{$perfType});
256
257
258
259
	    print "Push: OSD=$elem size=$aKey lat=".$dataHash{$elem}{$perfType}."\n" if (defined $opt_debug);
	    push (@latArray, $dataHash{$elem}{$perfType});
	    $count++;
	}
260
261
262
263
264
265
	my $aMinFrac = min( @{$fracTypeH{$aKey}});
	my $aMaxFrac = max( @{$fracTypeH{$aKey}});
	print "    min/max frac: ".$aMinFrac."/".$aMaxFrac."\n" if (defined $opt_debug);
	$minFrac = $aMinFrac if ( $aMinFrac < $minFrac );
	$maxFrac = $aMaxFrac if ( $aMaxFrac > $maxFrac );
	print "New min/max frac: ".$aMinFrac."/".$aMaxFrac."\n" if (defined $opt_debug);
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    }
}
print "Count: $count\n" if (defined $opt_debug);

# Make computation, return either average or standard deviation
my $retValue = -1.;
my $sumSq = 0.;
my $sumLin = 0.;
my $avgValue = -1.;
my $stdValue = -1.;
print $retValue unless ($count > 0);
for (@latArray) {
    $sumLin += $_;
    $sumSq  += $_ * $_;
    print "Elem: $_ lin=$sumLin sq=$sumSq\n" if (defined $opt_debug);
}
$avgValue = $sumLin / ($count + 0.);
$stdValue = sqrt(($sumSq / $count) - $avgValue * $avgValue);
print "Avg: $avgValue Std: $stdValue\n" if (defined $opt_debug);
285
286
287
288
289
290
if ( $fracType ne "" ) {
    if ($fracType eq 'minfrac') {
	print "$minFrac\n";
    } elsif ($fracType eq 'maxfrac') {
	print "$maxFrac\n";
    }
291
} else {
292
293
294
295
296
    if ($perfWhat eq 'dev') {
	print "$stdValue\n";
    } elsif ($perfWhat eq 'avg') {
	print "$avgValue\n";
    }
297
}
298

299
300
301
302
#
exit 0;