3 # ldev - parser for /etc/ldev.conf
7 use Getopt::Long qw/ :config posix_default no_ignore_case/;
9 $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin";
11 my $prog = basename($0);
14 Usage: $prog [OPTIONS]...
16 Parse ldev.conf and answer the following queries:
18 -h, --help Display this help.
19 -c, --config FILE Set path to config file.
20 -H, --hostname NAME Use NAME instead of local hostname for queries.
21 -p, --partner Print hostname of failover partner.
22 -l, --local Print labels for local devices.
23 -f, --foreign Print labels for foreign devices.
24 -a, --all Print labels for local and foreign devices.
25 -F, --filesys NAME Print labels for file system NAME.
26 -s, --sanity Sanity check config on this node.
27 -d, --device LABEL Print storage device of LABEL.
28 -j, --journal LABEL Print journal device of LABEL if it exists.
29 -r, --raidtab LABEL Print raidtab of LABEL if it exists.
30 -t, --type LABEL Print device type of LABEL, i.e. "zfs" or "md".
31 -z, --zpool LABEL Print zpool containing LABEL.
32 -R, --role ROLE Filter output based on role, i.e. mdt, ost, mgs.
33 CMD [ARGS] ... Run CMD in parallel for each device substituting:
34 %f=fsname %d=device %i=dec-index %n=main-nid %l=label
35 %t=srvtype %j=journal %I=hex-index %N=fail-nid %m=mgs-nid
36 %H=hostname %b=backing-fs
37 May be used in combination with -l, -f, -a, -F options.
41 elabel_uniq => "label used more than once",
42 epairwise => "local and foreign host not mapped to each other",
43 efieldcount => "line has less than the minimum number of fields (4)",
44 ekeyval => "malformed id=name",
59 sanity () if $conf{sanity};
60 exec_cmd () if $conf{execcmd};
61 query_partner () if $conf{partner};
62 query_local () if $conf{local};
63 query_foreign () if $conf{foreign};
64 query_all () if $conf{all};
65 query_device () if $conf{device};
66 query_journal () if $conf{journal};
67 query_raidtab () if $conf{raidtab};
68 query_type () if $conf{type};
69 query_zpool () if $conf{zpool};
70 query_filesys () if $conf{fsname};
87 $conf{config} = "/etc/ldev.conf";
88 $conf{nidsfile} = "/etc/nids";
89 $conf{hostname} = `uname -n`; chomp $conf{hostname};
99 "partner|p!" => \$conf{partner},
100 "all|a!" => \$conf{all},
101 "local|l!" => \$conf{local},
102 "foreign|f!" => \$conf{foreign},
103 "config|c=s" => \$conf{config},
104 "nidsfile|n=s" => \$conf{nidsfile},
105 "hostname|H=s" => \$conf{hostname},
106 "sanity|s!" => \$conf{sanity},
107 "device|d=s" => \$conf{device},
108 "journal|j=s" => \$conf{journal},
109 "raidtab|r=s" => \$conf{raidtab},
110 "type|t=s" => \$conf{type},
111 "zpool|z=s" => \$conf{zpool},
112 "role|R=s" => \$conf{role},
113 "filesys|F=s" => \$conf{fsname},
116 usage() if $help || !$rc;
118 log_fatal ("cannot read config file\n") if (! -r $conf{config});
121 $filters++ if ($conf{local});
122 $filters++ if ($conf{foreign});
123 $filters++ if ($conf{all});
124 $filters++ if ($conf{fsname});
125 log_fatal ("Only one of -l, -f, -a, or -F may be used.\n") if ($filters > 1);
128 $conf{execcmd} = " " . join " ", @ARGV;
131 parse_nids () if ($conf{execcmd} =~ /(%n|%N|%m)/);
138 my %label2local = ();
140 my %label2journal = ();
141 my %label2raidtab = ();
143 my %label2zpool = ();
144 my %filesys2mgs = ();
145 my %label2hostname = ();
146 my @local_labels = ();
147 my @foreign_labels = ();
150 open (CONF, "< $conf{config}") or log_fatal ("$conf{config}: $!\n");
159 my ($local, $foreign, $label, $dev, $j, $raidtab) = split;
160 if ($dev !~ /^\// && $dev =~ /^([^:]+):(.+)$/) {
165 eparse_line ($line, "efieldcount") if (!defined $dev);
166 eparse_line ($line, "epairwise") if (exists $l2f{$local}
167 && $l2f{$local} ne $foreign);
168 $l2f{$local} = $foreign;
170 eparse_line ($line, "elabel_uniq") if (exists $label2dev{$label}
171 || exists $label2local{$label});
173 $label2dev{$label} = $dev;
174 $label2local{$label} = $local;
175 $label2journal{$label} = $j if defined $j;
176 $label2raidtab{$label} = $raidtab if defined $raidtab;
177 $label2hostname{$label}=$local;
179 $label2type{$label} = $type;
180 if ($type eq "zfs" && $dev =~ m{^([^/]+)/[^/]+$}) {
181 $label2zpool{$label} = $1;
187 if ($label eq "MGS") {
191 /(\w+)-(OST|MDT|MGS)([0-9a-fA-F]{4})/, $label;
196 if ($nodetype eq "MGS" or $label eq "MGS") {
197 $filesys2mgs{$filesys} = $label;
200 if ($label eq "MGS") {
201 $global_mgs = $label;
204 next if $conf{role} and lc $conf{role} ne lc $nodetype;
206 if ($local eq $conf{hostname}) {
207 push @local_labels, $label;
208 } elsif ($foreign eq $conf{hostname}) {
209 push @foreign_labels, $label;
212 if ($conf{fsname} and $filesys eq $conf{fsname}) {
213 push @fs_labels, $label;
218 foreach (keys %l2f) {
219 my $foreign = $l2f{$_};
220 next if ($foreign eq "-");
221 eparse_str ($_, "epairwise")
222 unless (!exists $l2f{$foreign} or $l2f{$foreign} eq $_);
225 @{$conf{local_labels}} = @local_labels;
226 @{$conf{foreign_labels}} = @foreign_labels;
227 @{$conf{fs_labels}} = @fs_labels;
228 %{$conf{l2f}} = %l2f;
229 %{$conf{label2dev}} = %label2dev;
230 %{$conf{label2local}} = %label2local;
231 %{$conf{label2journal}} = %label2journal;
232 %{$conf{label2raidtab}} = %label2raidtab;
233 %{$conf{label2type}} = %label2type;
234 %{$conf{label2zpool}} = %label2zpool;
235 %{$conf{filesys2mgs}} = %filesys2mgs;
236 %{$conf{label2hostname}} = %label2hostname;
245 open (NIDS, "< $conf{nidsfile}") or log_fatal ("$conf{nidsfile}: $!\n");
252 my ($host, $nid, $morenids) = split (/\s+/, $_, 3);
254 log_fatal ("$conf{nidsfile} line $line: incomplete line\n");
256 $host2nid{$host} = $nid;
257 $nid2host{$nid} = $host;
258 map { $nid2host{$_} = $host; } split (/\s+/, $morenids);
262 %{$conf{host2nid}} = %host2nid;
263 %{$conf{nid2host}} = %nid2host;
268 my %l2f = %{$conf{l2f}};
269 my $hostname = $conf{hostname};
270 if (exists $l2f{$hostname} && $l2f{$hostname} ne "-") {
271 print "$l2f{$hostname}\n";
277 map { print "$_\n"; } @{$conf{local_labels}};
282 map { print "$_\n"; } @{$conf{foreign_labels}};
287 map { print "$_\n"; } @{$conf{fs_labels}};
298 my %label2dev = %{$conf{label2dev}};
300 if (exists $label2dev{$conf{device}}) {
301 print "$label2dev{$conf{device}}\n";
307 my %label2raidtab = %{$conf{label2raidtab}};
309 if (exists $label2raidtab{$conf{raidtab}}) {
310 print "$label2raidtab{$conf{raidtab}}\n";
316 my %label2journal = %{$conf{label2journal}};
318 if (exists $label2journal{$conf{journal}} &&
319 $label2journal{$conf{journal}} ne "-") {
320 print "$label2journal{$conf{journal}}\n";
326 my %label2type = %{$conf{label2type}};
328 if (exists $label2type{$conf{type}}) {
329 print "$label2type{$conf{type}}\n";
335 my %label2zpool = %{$conf{label2zpool}};
337 if (exists $label2zpool{$conf{zpool}}) {
338 print "$label2zpool{$conf{zpool}}\n";
346 my $bs = `blockdev --getss $dpath 2>/dev/null`; chomp $bs;
347 my $max512 = `blockdev --getsize $dpath 2>/dev/null`; chomp $max512;
348 if ($? == 0 && $bs > 0 && $max512 > 0) {
349 my $maxb = ($max512 / $bs) * 512;
350 my $count = 10 * 1024 * 1024 / $bs; # read first 10mb
351 my $dev = `readlink -f $dpath`; chomp $dev;
352 $count = $maxb if ($count > $maxb);
353 `dd if=$dev of=/dev/null bs=$bs count=$count >/dev/null 2>&1`;
363 my @local_labels = @{$conf{local_labels}};
364 my @foreign_labels = @{$conf{foreign_labels}};
365 my %label2dev = %{$conf{label2dev}};
366 my %label2journal = %{$conf{label2journal}};
368 foreach (@local_labels, @foreign_labels) {
369 my $lpath = "/dev/disk/by-label/$_";
370 my $dpath = $label2dev{$_};
371 my $jpath = $label2journal{$_};
374 log_error ("$lpath does not exist\n");
378 log_error ("$dpath does not exist\n");
380 } elsif (!dd_test ($dpath)) {
381 log_error ("$dpath failed dd test\n");
384 if (`readlink -f $lpath` ne `readlink -f $dpath`) {
385 log_error ("$lpath and $dpath point to different things\n");
390 log_error ("$jpath (journal for $label) does not exist\n");
392 } elsif (!dd_test ($jpath)) {
393 log_error ("$jpath failed dd test\n");
409 my $tmpfile = `mktemp \${TMPDIR:-/tmp}/ldev.XXXXXXXXXX`; chomp $tmpfile;
410 log_fatal ("failed to create $tmpfile\n") if (! -e $tmpfile);
413 my ($label, $cmd) = split (/\s+/, $_, 2);
414 my ($basecmd) = split (/\s+/, $cmd);
415 if (($pid = fork)) { # parent
416 $pid2label{$pid} = $label;
417 $pid2cmd{$pid} = $basecmd;
418 } elsif (defined $pid) { # child
419 #print STDERR "$label: running $cmd\n";
420 exec "($cmd 2>&1 || rm -f $tmpfile) | sed -e 's/^/$label: /'";
421 print STDERR "$label: exec $basecmd: $!\n"; unlink $tmpfile;
423 log_fatal ("label: fork: $!\n"); unlink $tmpfile;
426 while (($pid = wait) != -1) {
427 #print STDERR "$pid2label{$pid}: completed\n";
430 # sentinel is intact, so there were no errors
443 my %label2dev = %{$conf{label2dev}};
444 my %label2type = %{$conf{label2type}};
445 my %label2journal = %{$conf{label2journal}};
446 my %filesys2mgs = %{$conf{filesys2mgs}};
447 my %label2hostname = %{$conf{label2hostname}};
448 my %l2f = %{$conf{l2f}};
451 if ($conf{execcmd} =~ /%n/) {
452 my %host2nid = %{$conf{host2nid}};
453 if (!defined $host2nid{$conf{hostname}}) {
454 log_fatal ("%n used but no nid defined for this host\n");
456 $nid = $host2nid{$conf{hostname}};
458 if ($conf{execcmd} =~ /%N/) {
459 if (!defined $l2f{$conf{hostname}}) {
460 log_fatal ("%N used but foreign host is undefined\n");
462 my %host2nid = %{$conf{host2nid}};
463 if (!defined $host2nid{$l2f{$conf{hostname}}}) {
464 log_fatal ("%N used but foreign nid is undefined\n");
466 $failnid = $host2nid{$l2f{$conf{hostname}}};
469 if ($conf{foreign}) {
470 @labels = @{$conf{foreign_labels}};
471 } elsif ($conf{all}) {
472 @labels = (@{$conf{local_labels}}, @{$conf{foreign_labels}});
473 } elsif ($conf{fsname}) {
474 @labels = (@labels, @{$conf{fs_labels}});
475 push(@labels, $global_mgs) if ($global_mgs);
477 @labels = @{$conf{local_labels}};
481 /(\w+)-(OST|MDT|MGS)([0-9a-fA-F]{4})/;
484 my $type = $2; $type =~ tr/A-Z/a-z/;
486 my $decindex = hex($3);
488 my $cmd = $conf{execcmd};
489 my $device = $label2dev{$_};
490 if ($conf{execcmd} =~ /%j/ && !defined $label2journal{$_}) {
491 log_fatal ("%j used but no journal defined for $_\n");
493 my $journal = $label2journal{$_};
494 my $fstype = $label2type{$_};
495 if (!defined $fstype or $fstype ne "zfs") {
498 my $hostname = $label2hostname{$_};
504 if (exists $filesys2mgs{$fsname}) {
505 $mgs = $filesys2mgs{$fsname};
506 } elsif ($global_mgs) {
509 log_fatal ("$fsname has no MGS defined\n");
512 if (exists $label2hostname{$mgs}) {
513 $mgs_host = $label2hostname{$mgs};
515 log_fatal ("$mgs has no hostname defined\n");
518 my %host2nid = %{$conf{host2nid}};
519 if (!exists $host2nid{$mgs_host}) {
520 log_fatal ("$mgs and $mgs_host have no NID defined\n");
522 $mgsnid = $host2nid{$mgs_host};
525 $cmd =~ s/%f/$fsname/g; # %f = fsname
526 $cmd =~ s/%t/$type/g; # %t = server type
527 $cmd =~ s/%I/$hexindex/g;# %I = index (hex)
528 $cmd =~ s/%i/$decindex/g;# %i = index (dec)
529 $cmd =~ s/%l/$label/g; # %l = label
530 $cmd =~ s/%d/$device/g; # %d = device
531 $cmd =~ s/%j/$journal/g; # %j = journal device
532 $cmd =~ s/%n/$nid/g; # %n = nid
533 $cmd =~ s/%N/$failnid/g; # %N = failnid
534 $cmd =~ s/%m/$mgsnid/g; # %m = MGS nid
535 $cmd =~ s/%b/$fstype/g; # %b = backing file system type
536 $cmd =~ s/%H/$hostname/g;# %H = hostname
538 push @cmds, "$_ $cmd";
541 par_exec (@cmds) or log_fatal ("parallel command execution failed\n");
547 print STDERR "$usage";
551 sub log_msg { print STDERR "$prog: ", @_; }
552 sub log_error { log_msg ("Error: ", @_) }
553 sub log_fatal { log_msg ("Fatal: ", @_); exit 1; }
554 sub eparse_line { log_fatal ("$conf{config} line $_[0]: $eparse{$_[1]}\n"); }
555 sub eparse_str { log_fatal ("$conf{config}: $_[0]: $eparse{$_[1]}\n"); }