Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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