Skip to content

Commit

Permalink
Merge pull request #425 from mariux64/make-mxraid_assemble-journal-aware
Browse files Browse the repository at this point in the history
Make mxraid assemble journal aware
  • Loading branch information
thomas authored Dec 10, 2024
2 parents ce87988 + d11775d commit 447c2b8
Show file tree
Hide file tree
Showing 3 changed files with 367 additions and 56 deletions.
1 change: 1 addition & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ install_data pkgadmin/pkgadmin-update.service "$DESTDIR$systemdunitdi
install_data pkgadmin/pkgadmin-update.timer "$DESTDIR$systemdunitdir/pkgadmin-update.timer"
install_exec mxraid/mxraid "$DESTDIR$usr_sbindir/mxraid"
install_exec mxraid/mxraid_assemble "$DESTDIR$usr_sbindir/mxraid_assemble"
install_exec mxraid/mxraid_journal "$DESTDIR$usr_sbindir/mxraid_journal"
install_data mxraid/mxraid.service "$DESTDIR$systemdunitdir/mxraid.service"
install_data mxraid/mxraid.shutdown.service "$DESTDIR$systemdunitdir/mxraid.shutdown.service"
install_exec mxraid/mdcheck.safe "$DESTDIR$usr_bindir/mdcheck.safe"
Expand Down
168 changes: 112 additions & 56 deletions mxraid/mxraid_assemble
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import MxRaid::HostData;
import MxRaid::Utils;
import MxRaid::Color;

my $VERSION = '1.2';
my $VERSION = '1.3.0';

sub exec_usage {

Expand Down Expand Up @@ -112,11 +112,15 @@ if ($opts{d}) {

my $MDADM_CONF_BASE = '/dev/shm/mdadm.conf'; # config for mdadm, created with information from above

if ($ENV{MDADM_CONF_BASE}) { # option to override when debugging
$MDADM_CONF_BASE = $ENV{MDADM_CONF_BASE};
}

my $MDADM_ASSEMBLE_OPTIONS = '';

my $cd = MxRaid::ConfData->new($MDADMCONF_DB); $cd->verbose($VERBOSE); $cd->load();
check_enclosures(); # idle a bit if enclosures are attached ...
my $hd = MxRaid::HostData->new($cd); $hd->verbose($VERBOSE);
my $hd = MxRaid::HostData->new($cd); $hd->verbose($VERBOSE); $hd->load();
my $utils = MxRaid::Utils->new();

my $do_default_action = 1;
Expand All @@ -139,25 +143,26 @@ if ($opts{l}) {
for my $label (@labels) {
my %reg;
print "$label:\n";
for my $rec (@{$hd->configured_raids()->{$label}}) {
for my $rec (@{$hd->member_dev_recs($label)}) {
my $md = $hd->mddev_by_member()->{$rec->[0]};
$md = 'n/a' unless defined $md;
$reg{$md}+=1;
printf " %-5s %-9s %-16s '%s'\n", $md, @$rec[0..2];
printf " %-5s %-10s %-20s '%s'\n", $md, @$rec[0..2];
}
print " Note: this doesn't look like a standard configuration.\n" if scalar keys %reg != 1;
print "\n";
}
if ($VERBOSE >= 2) {
my @mounts = split m/\n/, `cat /proc/self/mounts`;
print "Disks not part of a configured SW-RAID:\n";
print "Disks/partitions not part of a configured SW-RAID:\n";
for my $rec (@{$hd->non_raid_disks()}) {
my $hint = '';
if ($hd->mddev_by_member()->{$rec->[0]}) {
$hint .= sprintf " ** part of %s, yet not in mxmd.conf", $hd->mddev_by_member()->{$rec->[0]};
$hint = sprintf " ***\n%22s- ", '';
$hint .= sprintf 'part of %s, yet not in mxmd.conf -', $hd->mddev_by_member()->{$rec->[0]};
}
$hint .= ' (mounted)' if scalar(grep {m|^/dev/$rec->[0]\d*\b|} @mounts);
printf " %-9s %-16s '%s'%s\n", @$rec[0..2], $hint;
printf " %-10s %-20s '%s'%s\n", @$rec[0..2], $hint;
}
print "\n";
}
Expand Down Expand Up @@ -253,6 +258,7 @@ if ($opts{n}) {
if ($do_default_action) {
create_mdadm_configs();
}

exit;

# prepare to assemble for the complete host
Expand Down Expand Up @@ -285,7 +291,6 @@ sub create_mdadm_configs {
}
my $res = $utils->list_differences( $cd->members($rlabel), $hd->member_serials($rlabel));
if ($res) {
# print Dumper $res;
# second array in $res must be empty, first array contains missing one(s), make shure ;)
die "# ERROR: $rlabel, possible software error, stopped" if scalar(@{$res->[1]});
warn sprintf "# NOTE: $rlabel, disk(s) missing: %s.\n", join(', ', @{$res->[0]}) if $VERBOSE >= 1;
Expand All @@ -295,10 +300,19 @@ sub create_mdadm_configs {
# some more checks if some/all arrays run already
if (scalar @{$hd->get_md_devices()}) {
my %running;
for my $dev (@{$hd->member_devs($rlabel)}) {
my @sys_info = glob("/sys/block/$dev/holders/md*");
for my $dev_rec (@{$hd->member_dev_recs($rlabel)}) {
my $dev = $dev_rec->[0];
my $parent = $dev_rec->[4];

my @sys_info = ();
if (!$parent) {
@sys_info = glob("/sys/block/$dev/holders/md*");
} else {
@sys_info = glob("/sys/block/$parent/$dev/holders/md*");
}

next if scalar(@sys_info) == 0;
die '# Fuck! Stopped' if scalar(@sys_info) > 1; # this will never happen ;)
die '# Ieeck! Stopped, duplicate entry' if scalar(@sys_info) > 1; # this will never happen ;)
$sys_info[0] =~ m/(md(\d+))$/;
my $md = $1;
$active_dev_no = $2;
Expand Down Expand Up @@ -397,13 +411,13 @@ sub create_mdadm_configs {
} else {
printf "mdadm -A /dev/md%d -c %s\n", $dev_no, $conf_fn;
}

} else {
printf "sudo mdadm -A /dev/md%d -c %s %s\n", $dev_no, $conf_fn, $MDADM_ASSEMBLE_OPTIONS;
}
}
} else {
warn "# NOTE: No SW-RAIDS configured on this host.\n" if $VERBOSE >= 1;
warn "# NOTE: No properly configured SW-RAIDS on this host.\n" if $VERBOSE >= 1;
}

}
Expand Down Expand Up @@ -457,20 +471,24 @@ exit;

$array_name = shift @rec;
chop $array_name; # remove the ':'

next unless @rec;

# accept sn#.0 for raw disks, but discard it
@rec = map { $_ =~ m/(.*)\.0$/ ? $1 : $_} @rec;

if (exists $self->{db}{$array_name}) {
warn "# WARN: database contains same array name more than once ($array_name)!\n" if $self->{verbose} >= 1;
push @faulty_names, $array_name;
}
$self->{db}{$array_name} = [ @rec ];
}
close DB;

for (@faulty_names) {
delete $self->{db}{$_};
warn "# NOTE: removed badly configured array '$_' from configuration.\n";
warn "# NOTE: removed badly defined array '$_' from configuration.\n";
}

my %tmp; # see if this gets expensive ...
for my $label (keys %{$self->{db}}) {
for (@{$self->{db}{$label}}) {
Expand Down Expand Up @@ -539,7 +557,7 @@ exit;
package MxRaid::HostData;

BEGIN {
$MxRaid::HostData::VERSION = '0.01';
$MxRaid::HostData::VERSION = '0.01';
%MxRaid::HostData::BAD_MODELS = ( # and the cure...
ST8000NM0065 => sub {substr $_[0], 0, 8}, # silly coding of serial
ST8000NM0075 => sub {substr $_[0], 0, 8},
Expand All @@ -561,19 +579,24 @@ exit;
$self->{config} = $conf;
$self->{verbose} = 0;
$self->{root_priv} = $<==0?1:0;
return $self;
}

sub load {
my $self = shift;
$self->discover_basic();
$self->discover_running();
return $self;
}

sub discover_basic {
my $self = shift;

# take only whole disks, no partitions
# only disks like 'sd', 'nvme'
my @disks = grep {m:/(sd[a-z]+|nvme\d+n\d+)$:} (
glob('/sys/block/sd[a-z]*'), glob('/sys/block/nvme[0-9]*')
glob('/sys/block/sd[a-z]*'),
glob('/sys/block/nvme[0-9]*')
);
my $diskinfo = $self->get_hd_info(\@disks);
my $diskinfo = $self->get_hd_info_extended_to_partitions(\@disks);
my @non_raid;
my %configured_raids;
my %raid_labels;
Expand All @@ -599,7 +622,7 @@ exit;
sub discover_running {
my $self = shift;

my @mds_found = (
my @mds_found = (
glob('/sys/block/md[0-9]'), # upper limit 128, and no leading zeroes
glob('/sys/block/md[0-9][0-9]'), # but who knows
glob('/sys/block/md[0-9][0-9][0-9]')); #
Expand All @@ -611,22 +634,23 @@ exit;

for my $m (@mds_found) {
$m =~ m,([^/]+)$,;
my $md = $1;
# alternative : glob("/sys/block/$md/slaves/*");
my @tmp = glob("/sys/block/*/holders/$md");
my $md_tmp = $1;
# alternative : glob("/sys/block/$md/slaves/*"); # hmm, guess this will be renamed sometime
my @tmp = ( glob("/sys/block/*/holders/$md_tmp"), glob("/sys/block/*/*/holders/$md_tmp") );
for (@tmp) {
$_ =~ m,/sys/block/(.+)/holders/(.+),;
push @{$md2hd{$2}}, $1;
die "# what, duplicate device?" if exists $hd2md{$1};
$hd2md{$1} = $2;
$md_tmp = $2;
my $dev_tmp = $1; # can be 'sda/sda1'
$dev_tmp = ( split(m|/|, $dev_tmp) )[-1];
push @{$md2hd{$md_tmp}}, $dev_tmp;
die "# what, duplicate device?" if exists $hd2md{$md_tmp}; # this should never happen ...
$hd2md{$dev_tmp} = $md_tmp;
}
}

for my $k (keys %md2hd) {
my @tmp = @{$md2hd{$k}};
@tmp = map { '/sys/block/' . $_ } @tmp; # redo ...
my $hd_info = $self->get_hd_info(\@tmp);
# printf "%s: %s\n", $k, join(' ', @$hd_info);
my $hd_info = $self->get_hd_info_from_used_devices(\@tmp);
$md2info{$k} = [ @$hd_info ];

for (@$hd_info) {
Expand All @@ -636,7 +660,6 @@ exit;

}


$self->member_info_by_mddev(\%md2info);
$self->mddev_by_member(\%hd2md);
$self->member_dev_serial_cross_lookup(\%cross_lookup);
Expand All @@ -661,11 +684,10 @@ exit;
$self->{configured_raids};
}

sub member_serials {
sub member_dev_recs {
my $self = shift;
my $label = shift;
my $di = $self->{configured_raids}{$label};
[ map {$_->[1]} @$di ];
$self->{configured_raids}{$label};
}

sub member_devs {
Expand All @@ -675,6 +697,13 @@ exit;
[ map {$_->[0]} @$di ];
}

sub member_serials {
my $self = shift;
my $label = shift;
my $di = $self->{configured_raids}{$label};
[ map {$_->[1]} @$di ];
}

sub member_info_by_mddev {
my $self = shift;
$self->{member_info_by_mddev} = shift if @_;
Expand All @@ -698,54 +727,81 @@ exit;
return [ sort keys %{$self->{member_info_by_mddev}} ];
}

# 'shortcut'
sub get_hd_serial {
# Careful here: The device list consits of device names as keys sda, sdb, ... ,
# ------------- and possibly sday3, sday4, ... So, either raw or partitioned.
# Raw devices are found at /sys/block/sda, partitions under /sys/block/sdb/sdb1

sub get_hd_info_extended_to_partitions {
my $self = shift;
my $dev_list = shift;
return $self->get_hd_info($dev_list, 1);
}

sub get_hd_info_from_used_devices {
my $self = shift;
$self->get_hd_info($_[0], 1)
my $dev_list = shift;
return $self->get_hd_info($dev_list, 0);
}

sub get_hd_info {
my $self = shift;
my $dev_list = shift;
my $short_info = shift;
my $lookup_partitions = shift;

my @ret;
my $num = scalar @$dev_list;
for (my $i=0; $i<$num; $i++) {
my ($dk) = $dev_list->[$i] =~ m|([^/]+)$|; # '/sys/block/sda' -> 'sda'
my $dk = $dev_list->[$i];
$dk =~ s|^/sys/block/||; # '/sys/block/sda' -> 'sda' or 'sda/sda2'

my $sysprefix = '/sys/block/'.$dk;
my $sysprefix_part = $sysprefix;

if (not -d $sysprefix) { # then it is a partition-key, not a raw device
my @chk = glob('/sys/block/*/'.$dk);
if (not @chk or scalar(@chk) > 1) { die '# Error: Expected an uniqe parent device for '.$dk; }
my @tmp = split m|/|, $chk[0];
$sysprefix_part = '/sys/block/'.$tmp[3].'/'.$dk;
$sysprefix = '/sys/block/'.$tmp[3];
}

my $model = sys_fs_get_prop('/sys/block/'.$dk.'/device/model');
my $sizeb = sys_fs_get_prop('/sys/block/'.$dk.'/size') * 512;
my $model = sys_fs_get_prop($sysprefix.'/device/model');
my $sizeb = sys_fs_get_prop($sysprefix_part.'/size') * 512;

# if (! -e '/sys/block/'.$dk.'/device/vpd_pg80') {
# warn "# Note failed to read serial via sysfs ($dk).\n" if $self->{verbose};
# next;
# }
my @partitions;
if ($lookup_partitions) {
@partitions = glob('/sys/block/'.$dk.'/'.$dk.'*'); # find /sys/block/sda/sda1
@partitions = map { m|$dk/$dk(.+)$| } @partitions; # get the partition id (Mind: NVMEs use 'pX')
# NB: if it has partitions, then the whole disk should never appear as disk key ... 8)
}

my $serial_number;
if (-e '/sys/block/'.$dk.'/device/vpd_pg80') { # exists even for ahci attached disks
if (-e $sysprefix.'/device/vpd_pg80') { # exists even for ahci attached disks
# Vital Product Data, page xyz
my $vpd_pg80 = parse_vpd_pg80(sys_fs_get_prop('/sys/block/'.$dk.'/device/vpd_pg80'));
my $vpd_pg80 = parse_vpd_pg80(sys_fs_get_prop($sysprefix.'/device/vpd_pg80'));
if (defined $MxRaid::HostData::BAD_MODELS{uc($model)}) {
$vpd_pg80 = $MxRaid::HostData::BAD_MODELS{uc($model)}($vpd_pg80);
}
$serial_number = $vpd_pg80;
} elsif (-e '/sys/block/'.$dk.'/device/serial') { # nvme disks are supposed to have this
$serial_number = sys_fs_get_prop('/sys/block/'.$dk.'/device/serial');
} elsif (-e $sysprefix.'/device/serial') { # nvme disks are supposed to have this
$serial_number = sys_fs_get_prop($sysprefix.'/device/serial');
$serial_number =~ s/^\s+//;
} else {
if ($self->{root_priv}) {
$serial_number = smartctl_info('/dev/'.$dk, 'Serial_Number');
} else {
warn "# You must be root to query '$dk'.\n" if $self->{verbose} >= 1;
warn "# NOTE: You must be root to query '$dk'.\n" if $self->{verbose} >= 1;
}
}

if (defined $serial_number) {
if ($short_info) {
push @ret, $serial_number;
if (scalar @partitions) {
for my $part (@partitions) {
my $psize = sys_fs_get_prop($sysprefix_part.'/size');
push @ret, [$dk.$part, $serial_number.'.'.$part, $model, $psize, $dk];
}
} else {
push @ret, [$dk, $serial_number, $model, $sizeb];
push @ret, [$dk, $serial_number, $model, $sizeb, ''];
}
}
}
Expand Down Expand Up @@ -787,7 +843,7 @@ exit;
# alternative if no vpd_pg80 avail
sub smartctl_info {
my $dev = shift;
my @keys = @_;
my @keys = @_;
# smartctl -i /dev/sda | grep 'Serial Number:'
my @res = split m/\n/, `smartctl -i $dev`;

Expand Down Expand Up @@ -821,7 +877,7 @@ exit;
sub verbose {
my $self = shift;
$self->{verbose} = shift if @_;
$self->{verbose}
$self->{verbose};
}
}

Expand Down
Loading

0 comments on commit 447c2b8

Please sign in to comment.