Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / ceph-lazy / ceph-lazy
1 #!/bin/bash
2 #
3 #  ceph-lazy : Be efficient, be lazy !
4 #
5 #  Author: Gregory Charot <gcharot@redhat.com>
6 #
7 #  This is free software; you can redistribute it and/or
8 #  modify it under the terms of the GNU Lesser General Public
9 #  License as published by the Free Software Foundation; either
10 #  version 2.1 of the License, or (at your option) any later version.
11 #
12
13 # Specify exta option for ceph like the username/keyring/etc. Can also be done with CEPH_ARGS global variable
14 #CEPH_OPT="-n client.username"
15 VERSION="1.1.2"
16
17 #
18 #  Print info message to stderr
19 #
20
21 function echoinfo() {
22   printf "INFO: %s\n" "$*" >&2;
23 }
24
25
26 #
27 #  Print error message to stderr
28 #
29
30 function echoerr() {
31   printf "ERROR: %s\n" "$*" >&2;
32 }
33
34
35 function help() {
36   >&2 echo "Usage : ceph-lazy [-d | -h] [command] [parameters]
37
38 Ceph complex quering tool - Version $VERSION
39
40 OPTIONS
41 ========
42     -d          Activate debug mode
43     -h          Print help
44
45 COMMANDS
46 =========
47
48     Host
49     -----
50     host-get-osd      hostname                      List all OSD IDs attached to a particular node.
51     host-get-nodes                                  List all storage nodes.
52     host-osd-usage    hostname                      Show total OSD space usage of a particular node (-d for details).
53     host-all-usage                                  Show total OSD space usage of each nodes (-d for details)
54
55     Placement groups
56     -----------------
57     pg-get-host       pgid                          Find PG storage hosts (first is primary)
58     pg-most-write                                   Find most written PG (nb operations)
59     pg-less-write                                   Find less written PG (nb operations)
60     pg-most-write-kb                                Find most written PG (data written)
61     pg-less-write-kb                                Find less written PG (data written)
62     pg-most-read                                    Find most read PG (nb operations)
63     pg-less-read                                    Find less read PG (nb operations)
64     pg-most-read-kb                                 Find most read PG (data read)
65     pg-less-read-kb                                 Find less read PG (data read)
66     pg-empty                                        Find empty PGs (no stored object)
67
68     RBD
69     ----
70     rbd-prefix        pool_name image_name          Return RBD image prefix
71     rbd-count         pool_name image_name          Count number of objects in a RBD image
72     rbd-host          pool_name image_name          Find RBD primary storage hosts
73     rbd-osd           pool_name image_name          Find RBD primary OSDs
74     rbd-size          pool_name image_name          Print RBD image real size
75     rbd-all-size      pool_name                     Print all RBD images size (Top first)
76
77     OSD
78     ----
79     osd-most-used                                   Show the most used OSD (capacity)
80     osd-less-used                                   Show the less used OSD (capacity)
81     osd-get-ppg       osd_id                        Show all primaries PGS hosted on a OSD
82     osd-get-pg        osd_id                        Show all PGS hosted on a OSD
83
84     Objects
85     --------
86     object-get-host   pool_name object_id           Find object storage hosts (first is primary)
87   "
88
89 }
90
91 #
92 #  Check dependencies
93 #
94 function check_requirements()
95 {
96
97   # List of command dependencies
98   local bin_dep="ceph rados rbd osdmaptool jq"
99
100   for cmd in $bin_dep; do
101     [ $DEBUG -eq 1 ] && echoinfo "Checking for $cmd..."
102     $cmd --version  >/dev/null 2>&1 || { echoerr "$cmd cannot be found... Aborting."; return 1; }
103   done
104
105   CEPH="ceph $CEPH_OPT"
106
107   [ $DEBUG -eq 1 ] && echoinfo "Checking Ceph connectivity & basic permissions..."
108
109   if ! $CEPH -s &> /dev/null; then
110     echoerr "Cannot connect to cluster, please check your username & permissions"
111     echoerr "Command $CEPH -s failed"
112     return 1
113   fi
114
115   JQ="jq -M --raw-output"
116 }
117
118 #
119 #  Print the host that hosts a specific PG
120 #
121 function find_host_from_pg() {
122
123   if [ $# -eq 1 ]; then
124     local PGID=$1
125   else
126     echoerr "This command requires one argument"
127     help
128     exit 1
129   fi
130
131   [ $DEBUG -eq 1 ] && echoinfo "PG $PGID has been found at (first is primary) : "
132
133   for osd in $($CEPH pg $PGID  query | $JQ -cr .up[]); do
134         echo -n "OSD:osd.$osd | Host:"
135         $CEPH osd find $osd --format json 2> /dev/null | $JQ .crush_location.host
136   done
137 }
138
139
140 #
141 #  Print the host that hosts a specific object
142 #
143 function find_host_from_object() {
144
145   if [ $# -eq 2 ]; then
146     local pool=$1
147     local objid=$2
148   else
149     echoerr "This command requires two arguments"
150     help
151     exit 1
152   fi
153
154   local pgid=$($CEPH osd map $pool $objid --format json 2> /dev/null | $JQ -cr .pgid)
155
156   [ $DEBUG -eq 1 ] && echoinfo $objid found into PG $pgid
157
158   while read host; do
159     echo "PG:$pgid | $host"
160   done < <(find_host_from_pg $pgid)
161 }
162
163
164 #
165 #  Print all primary pgs hosted by an OSD
166 #
167 function find_prim_pg_from_osd() {
168
169   if [ $# -eq 1 ]; then
170     local posd=$1
171   else
172     echoerr "This command requires one argument"
173     help
174     exit 1
175   fi
176
177   [ $DEBUG -eq 1 ] && echoinfo "Looking for primary PGs beloging to OSD $posd"
178   $CEPH pg dump pgs --format json 2>/dev/null | $JQ --argjson posd $posd '.[] | select(.acting_primary==$posd).pgid'
179 }
180
181
182 #
183 #  Print all pgs (primay & secondary) hosted by an OSD
184 #
185 function find_all_pg_from_osd() {
186
187   if [ $# -eq 1 ]; then
188     local osd=$1
189   else
190     echoerr "This command requires one argument"
191     help
192     exit 1
193   fi
194
195   [ $DEBUG -eq 1 ] && echoinfo "Looking for all PGs mapped to OSD $osd"
196   $CEPH pg dump pgs --format json 2> /dev/null | $JQ -M --argjson osd $osd '.[] | select(.up[]==$osd).pgid'
197 }
198
199
200 #
201 #  Check if a given image exists
202 #
203 function check_rbd_exists(){
204
205   pool=$1
206   rbd=$2
207
208   if ! rbd info -p $pool $rbd &> /dev/null; then
209     echoerr "Unable to find image $pool/$rbd"
210     exit 1
211   fi
212 }
213
214
215 #
216 #  Return RBD prefix from image name
217 #
218 function get_rbd_prefix() {
219
220   if [ $# -eq 2 ]; then
221     local pool=$1
222     local rbd=$2
223   else
224     echoerr "This command requires two arguments"
225     help
226     exit 1
227   fi
228
229   check_rbd_exists $pool $rbd
230
231   local prefix=$(rbd --image $rbd -p $pool info --format json 2> /dev/null | jq --raw-output .block_name_prefix)
232   if [ -z $prefix ]; then
233     echoerr "Unable to find RBD Prefix for image $pool/$rbd"
234     exit 1
235   else
236     echo $prefix
237   fi
238
239 }
240
241
242 #
243 #  Count number of object in a RBD image
244 #
245 function count_rbd_object() {
246
247   if [ $# -eq 2 ]; then
248     local pool=$1
249     local rbd=$2
250   else
251     echoerr "This command requires two arguments"
252     help
253     exit 1
254   fi
255
256   check_rbd_exists $pool $rbd
257
258   local rbd_prefix=$(get_rbd_prefix $pool $rbd)
259
260   [ $DEBUG -eq 1 ] && echoinfo "RBD image $pool/$rbd has prefix $rbd_prefix; now couning objects..."
261
262   local nb_obj=$(rados -p $pool ls | grep $rbd_prefix | wc -l)
263
264   [ $DEBUG -eq 1 ] && echoinfo "RBD image $pool/$rbd has $nb_obj objects"
265   echo $nb_obj
266 }
267
268
269 #
270 #  Find primary storage host for a given RBD image
271 #
272 function find_prim_host_from_rbd() {
273
274  if [ $# -eq 2 ]; then
275     local pool=$1
276     local rbd=$2
277   else
278     echoerr "This command requires two arguments"
279     help
280     exit 1
281   fi
282
283   check_rbd_exists $pool $rbd
284
285   local osd="null"
286   local osdmap_t=$(mktemp)
287   local osdtree_t=$(mktemp)
288   # Get RBD image prefix
289   local rbd_prefix=$(get_rbd_prefix $pool $rbd)
290 # Exit if we received an empty prefix
291   [ -z $rbd_prefix ] && exit 1
292
293 # Get pool ID from pool name
294   local pool_id=$(ceph osd lspools -f json | $JQ  -M --arg pool $pool '.[]|select(.poolname==$pool).poolnum')
295   
296   [ $DEBUG -eq 1 ] && echoinfo "RBD image $pool/$rbd has prefix $rbd_prefix; now finding primary host..."
297
298   [ $DEBUG -eq 1 ] && echoinfo "Dumping OSD map to $osdmap_t"
299   if ! $CEPH osd getmap > $osdmap_t 2> /dev/null; then
300     echoerr "Failed to retrieve OSD map"
301     exit 1
302   fi
303
304   [ $DEBUG -eq 1 ] && echoinfo "Dumping OSD tree to $osdtree_t"
305
306   if ! $CEPH osd tree --format json > $osdtree_t; then
307     echoerr "Failed to retrieve OSD tree"
308     exit 1
309   fi
310
311   [ $DEBUG -eq 1 ] && echoinfo "Looking for hosts..."
312
313 # For each object in the RBD image
314   for obj in $(rados -p $pool ls | grep $rbd_prefix);
315     do
316 # Map object to osd. osdmaptoot does not support json output so using dirty sed.
317       osd=$(osdmaptool  --test-map-object $obj --pool $pool_id $osdmap_t 2>/dev/null | sed -r 's/.*\[([[:digit:]]+),.*/\1/' | grep -v osdmaptool)
318 # Map osd to host
319       $JQ --argjson osd $osd  '.nodes[] | select(.type=="host") | select(.children[] == $osd).name' $osdtree_t
320   done | sort -u
321
322 # Cleaning files
323   rm -f $osdtree_t $osdmap_t
324 }
325
326
327 #
328 #  Find primary OSDs for a given RBD image
329 #
330 function find_prim_osd_from_rbd() {
331
332  if [ $# -eq 2 ]; then
333     local pool=$1
334     local rbd=$2
335   else
336     echoerr "This command requires two arguments"
337     help
338     exit 1
339   fi
340
341   check_rbd_exists $pool $rbd
342
343   local osd="null"
344   local osdmap_t=$(mktemp)
345   local osdtree_t=$(mktemp)
346   # Get RBD image prefix
347   local rbd_prefix=$(get_rbd_prefix $pool $rbd)
348
349 # Exit if we received an empty prefix
350   [ -z $rbd_prefix ] && exit 1
351
352   [ $DEBUG -eq 1 ] && echoinfo "RBD image $pool/$rbd has prefix $rbd_prefix; now finding primary OSDs..."
353
354   [ $DEBUG -eq 1 ] && echoinfo "Dumping OSD map to $osdmap_t"
355   if ! $CEPH osd getmap > $osdmap_t; then
356     echoerr "Failed to retrieve OSD map"
357     exit 1
358   fi
359
360 # For each object in the RBD image
361   for obj in $(rados -p $pool ls | grep $rbd_prefix);
362     do
363 # Map object to osd. osdmaptoot does not support json output so using dirty sed.
364       osd=$(osdmaptool  --test-map-object $obj $osdmap_t 2>/dev/null | sed -r 's/.*\[([[:digit:]]+),.*/\1/' | grep -v osdmaptool)
365       echo "osd.${osd}"
366   done | sort -u
367
368 # Cleaning files
369   rm -f $osdmap_t
370 }
371
372
373 #
374 #  Print RBD image real size - Source http://ceph.com/planet/real-size-of-a-ceph-rbd-image/
375 #
376
377 function print_rbd_real_size {
378
379    if [ $# -eq 2 ]; then
380     local pool=$1
381     local rbd=$2
382   else
383     echoerr "This command requires two arguments"
384     help
385     exit 1
386   fi
387
388   [ $DEBUG -eq 1 ] && echoinfo "Checking if RBD image exists..."
389
390   check_rbd_exists $pool $rbd
391
392   rbd diff $pool/$rbd | awk '{ SUM += $2 } END { print SUM/1024/1024 " MB" }'
393
394 }
395
396
397 #
398 #  Print all RBD image real sizes - Top first
399 #
400
401 function list_all_rbd_real_size {
402
403   if [ $# -eq 1 ]; then
404     local pool=$1
405   else
406     echoerr "This command requires one argument"
407     help
408     exit 1
409   fi
410
411   [ $DEBUG -eq 1 ] && echoinfo "Looking for RBD images in pool $pool"
412
413   while read rbd; do
414     [ $DEBUG -eq 1 ] && echoinfo "Inspecting image $rbd"
415     rbd diff $pool/$rbd | awk -v rbd="$rbd" '{ SUM += $2 } END { print SUM/1024/1024 " MB - " rbd }'
416   done < <(rbd -p $pool ls) | sort -rV
417 }
418
419
420 #
421 #  Print OSDs belonging to a particular storage host
422 #
423
424 function list_osd_from_host() {
425
426   if [ $# -eq 1 ]; then
427     local host=$1
428   else
429     echoerr "This command requires one argument"
430     help
431     exit 1
432   fi
433
434   $CEPH osd tree --format json-pretty 2> /dev/null | $JQ  --arg host $host '.nodes[] | select(.type=="host") | select(.name == $host).children[]' | sort -V
435
436 }
437
438
439 #
440 #  List all OSD nodes
441 #
442
443 function list_all_nodes() {
444
445
446   $CEPH osd tree --format json | $JQ  -M --raw-output '.nodes[] | select(.type=="host") | .name' | sort -V
447
448 }
449
450
451 #
452 #  Print Total OSD usage of a particular storage host
453 #
454
455 function show_host_osd_usage() {
456
457   if [ $# -eq 1 ]; then
458     local host=$1
459   else
460     echoerr "This command requires one argument"
461     help
462     exit 1
463   fi
464
465   local pgmap_t=$(mktemp)
466
467   local osd_used_kb=0
468   local total_used_kb=0
469
470   local total_available_kb=0
471   local osd_available_kb=0
472
473   local total_size_kb=0
474   local osd_size_kb=0
475   local nb_osd=0
476
477   [ $DEBUG -eq 1 ] && echoinfo "Dumping PG map..."
478   if ! $CEPH pg dump osds --format json 2>/dev/null > $pgmap_t; then
479     echoerr "Failed to retrieve PG map"
480     exit 1
481   fi
482
483   [ $DEBUG -eq 1 ] && echoinfo "Looking for all OSDs on host $host..."
484
485   for osd in $(list_osd_from_host $host); do
486
487     osd_used_kb=$($JQ --argjson osd $osd '.[] | select(.osd == $osd).kb_used' $pgmap_t)
488     osd_available_kb=$($JQ --argjson osd $osd '.[] | select(.osd == $osd).kb_avail' $pgmap_t)
489     osd_size_kb=$($JQ --argjson osd $osd '.[] | select(.osd == $osd).kb' $pgmap_t)
490
491     [ $DEBUG -eq 1 ] && echoinfo "OSD:$osd | Size:$(echo "scale=1;$osd_size_kb/1024/1024" | bc -l)GB | Used:$(echo "scale=1;$osd_used_kb /1024/1024" | bc -l)GB | Available:$(echo "scale=1;$osd_available_kb/1024/1024" | bc -l)GB"
492
493     let "total_used_kb=total_used_kb+osd_used_kb"
494     let "total_available_kb=total_available_kb+osd_available_kb"
495     let "total_size_kb=total_size_kb+osd_size_kb"
496     let "nb_osd++"
497
498   done
499
500   echo "Host:$host | OSDs:$nb_osd | Total_Size:$(echo "scale=1;$total_size_kb/1024/1024" | bc -l)GB | Total_Used:$(echo "scale=1;$total_used_kb /1024/1024" | bc -l)GB | Total_Available:$(echo "scale=1;$total_available_kb/1024/1024" | bc -l)GB"
501
502   rm -f $pgmap_t
503 }
504
505
506 #
507 #  Print Total OSD usage of all nodes
508 #
509
510 function list_all_nodes_osd_usage() {
511
512
513   for host in $(list_all_nodes); do
514
515     [ $DEBUG -eq 1 ] && echoinfo "Looking at node $host..."
516
517     show_host_osd_usage $host
518   done
519
520 }
521
522
523 #
524 #  Find most used (space) OSD
525 #
526
527 function find_most_used_osd() {
528
529   local osd=$($CEPH pg dump osds --format json 2> /dev/null| $JQ 'max_by(.kb_used) | .osd')
530   local host=$($CEPH osd find $osd 2> /dev/null | $JQ .crush_location.host)
531
532   echo "OSD:osd.${osd} | host:$host"
533 }
534
535
536 #
537 #  Find less used (space) OSD
538 #
539
540 function find_less_used_osd() {
541
542   local osd=$($CEPH pg dump osds --format json 2> /dev/null| $JQ 'min_by(.kb_used) | .osd')
543   local host=$($CEPH osd find $osd 2> /dev/null | $JQ .crush_location.host)
544
545   echo "OSD:osd.${osd} | host:$host"
546 }
547
548
549 #
550 #  Query PG stats
551 #
552
553 function pg_stat_query() {
554
555   if [ $# -eq 1 ]; then
556     local query_type=$1
557   else
558     echoerr "This command requires one argument"
559     help
560     exit 1
561   fi
562
563   local pgmap_t=$(mktemp)
564
565   [ $DEBUG -eq 1 ] && echoinfo "Dumping PG map..."
566   if ! $CEPH pg dump pgs --format json 2>/dev/null > $pgmap_t; then
567     echoerr "Failed to retrieve PG map"
568     exit 1
569   fi
570
571   local pgid=$($JQ --arg query_type $query_type "$query_type" $pgmap_t)
572   [ $DEBUG -eq 1 ] && echoinfo "Found PGID $pgid"
573
574   local osd=$($JQ --arg pgid $pgid '.[] | select(.pgid == $pgid).acting_primary' $pgmap_t)
575   [ $DEBUG -eq 1 ] && echoinfo "Found OSD $osd"
576
577   local host=$($CEPH osd find $osd --format json 2> /dev/null | $JQ .crush_location.host)
578   [ $DEBUG -eq 1 ] && echoinfo "Found host $host"
579
580   echo "PG:$pgid | OSD:osd.$osd | Host:$host"
581
582   rm -f $pgmap_t
583 }
584
585
586 #
587 #  Find empty pgs (no object stored)
588 #
589
590 function find_empty_pg() {
591
592   $CEPH pg dump pgs --format json 2>/dev/null | $JQ '.[] | select(.stat_sum.num_objects == 0).pgid'
593
594 }
595
596
597 #
598 #  MAIN
599 #
600
601
602 # Print help if no argument is given
603 if [ $# -eq 0 ]; then
604   help
605   exit 1
606 fi
607
608 # Activate debug mode if -d is specified as first parameter
609 if [ "$1" = "-d" ]; then
610   echoinfo "Debug mode activated"
611   DEBUG=1
612   shift
613 else
614   DEBUG=0
615 fi
616
617
618 # Check if all requirements are met
619 check_requirements || exit 1
620
621
622 # Call proper function
623 case $1 in
624   "-h")
625     help
626     exit 0
627     ;;
628   "host-get-osd")
629     list_osd_from_host $2
630     ;;
631   "host-get-nodes")
632     list_all_nodes
633     ;;
634   "host-osd-usage")
635     show_host_osd_usage $2
636     ;;
637   "host-all-usage")
638     list_all_nodes_osd_usage
639     ;;
640   "pg-get-host")
641     find_host_from_pg $2
642     ;;
643   "pg-most-write")
644     pg_stat_query "max_by(.stat_sum.num_write).pgid"
645     ;;
646   "pg-less-write")
647     pg_stat_query "min_by(.stat_sum.num_write).pgid"
648     ;;
649   "pg-most-write-kb")
650     pg_stat_query "max_by(.stat_sum.num_write_kb).pgid"
651     ;;
652   "pg-less-write-kb")
653     pg_stat_query "min_by(.stat_sum.num_write_kb).pgid"
654     ;;
655   "pg-most-read")
656     pg_stat_query "max_by(.stat_sum.num_read).pgid"
657     ;;
658   "pg-less-read")
659     pg_stat_query "min_by(.stat_sum.num_read).pgid"
660     ;;
661   "pg-most-read-kb")
662     pg_stat_query "max_by(.stat_sum.num_read_kb).pgid"
663     ;;
664   "pg-less-read-kb")
665     pg_stat_query "min_by(.stat_sum.num_read_kb).pgid"
666     ;;
667   "rbd-prefix")
668     get_rbd_prefix $2 $3
669     ;;
670   "rbd-count")
671     count_rbd_object $2 $3
672     ;;
673   "rbd-host")
674     find_prim_host_from_rbd $2 $3
675     ;;
676   "rbd-osd")
677     find_prim_osd_from_rbd $2 $3
678     ;;
679   "rbd-size")
680     print_rbd_real_size $2 $3
681     ;;
682   "rbd-all-size")
683     list_all_rbd_real_size $2
684     ;;
685   "osd-most-used")
686     find_most_used_osd
687     ;;
688   "osd-less-used")
689     find_less_used_osd
690     ;;
691   "osd-get-ppg")
692     find_prim_pg_from_osd $2
693     ;;
694   "osd-get-pg")
695     find_all_pg_from_osd $2
696     ;;
697   "pg-empty")
698     find_empty_pg
699     ;;
700   "object-get-host")
701     find_host_from_object $2 $3
702     ;;
703   *)
704     echoerr "Unknown command : $1"
705     help
706     exit 1
707     ;;
708 esac
709