Skip to content

Mxraid assemble enhancements #67

Merged
merged 7 commits into from
Jan 23, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
150 changes: 99 additions & 51 deletions mxraid/mxraid_assemble
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,40 @@ import MxRaid::HostData;
import MxRaid::Utils;
import MxRaid::Color;

my $VERSION = '1.0';
my $VERSION = '1.1';

sub exec_usage {

my ($prog) = $0 =~ m|([^/]+)$|;

print <<"__HELP";

$prog usage:
$prog [options...]

$prog [options...]

default is to print assembly commands when run w/o options.

options:

-a assemble arrays when run as root
-c check database
-d file alternative database file
-h print this help and exit
-l list arrays on host, with -v will also show candidates
-m monochrome warnings (for the purists)
-n label print config records for new assemblies on host
-q be quiet
-v be more verbose
-V print Version information and exit

-a assemble arrays when run as root
-c check database
-d file alternative database file
-h print this help and exit
-l list arrays on host, with -v will also show candidates
-m monochrome warnings (for the purists)
-n label print config records for new assemblies on host
-q be quiet
-r l:d(:m) config hints, if not a RAID 6 with 16 disks (level, disks, match)
-v be more verbose
-V print Version information and exit

example (list state and assist in assembling a RAID1):

mxraid_assemble -lv -n D0014 -r 1:2:flash


__HELP

=things to come
-1 run script even if only 1 disk is present, well ...
=cut

exit 0;
}
Expand All @@ -63,6 +65,13 @@ sub exec_version {
}

sub check_enclosures {

# Slow down if known HBAs are in use, possibly there is a lazy enclosure attached.
# This might be skipped if /proc/uptime is large enough?
if (-e '/sys/module/aacraid/version' or -e '/sys/module/smartpqi/version') {
usleep(0.3 * 1e6);
}

glob('/sys/class/enclosure/*') or return;

my $cnt_0 = () = (glob('/sys/block/sd[a-z]'), glob('/sys/block/sd[a-z][a-z]'));
Expand All @@ -81,7 +90,7 @@ sub check_enclosures {
my $ROOT=$<==0?1:0;

my %opts;
getopts('acd:hlmn:qvV', \%opts) or die "# ERROR: getopts failed, try -h.\n"; # Values in %opts
getopts('acd:hlmn:qr:vV', \%opts) or die "# ERROR: getopts failed, try -h.\n"; # Values in %opts
exec_usage if $opts{h};
exec_version if $opts{V};

Expand Down Expand Up @@ -134,15 +143,17 @@ if ($opts{l}) {
my $md = $hd->mddev_by_member()->{$rec->[0]};
$md = 'n/a' unless defined $md;
$reg{$md}+=1;
printf " %-5s %5s %-16s '%s'\n", $md, @$rec;
printf " %-5s %-9s %-16s '%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";
for my $rec (@{$hd->non_raid_disks()}) {
printf " %5s %-16s '%s'\n", @$rec;
my $mnt = scalar(grep {m|^/dev/$rec->[0]\d*\b|} @mounts) ? ' (mounted)':'';
printf " %-9s %-16s '%s'%s\n", @$rec[0..2], $mnt;
}
print "\n";
}
Expand All @@ -152,56 +163,86 @@ if ($opts{n}) {
my $color = MxRaid::Color->new();
$do_default_action = 0;

my ($entry, $cnt) = ('',0);
my @candidates;
my ($entry, $cnt, $size) = ('', 0, 0);
my ($level, $num_wanted, $match) = (6, 16, undef);
my $chunk_size = 512;
my $dev_no = 0;
my @mounts = split m/\n/, `cat /proc/self/mounts`;
my @candidates;

if ($opts{r}) {
($level,$num_wanted,$match) = split m/:/, $opts{r};
}
$entry .= $opts{n}.':';
for my $rec (@{$hd->non_raid_disks()}) {
my $dev = $rec->[0];
next unless $dev =~ m/sd[a-z]+/;
next unless $dev =~ m/(sd[a-z]+|nvme\d+n\d+)/;
if (grep {m|^/dev/$dev\d*\b|} @mounts) {
warn "# NOTE: /dev/$dev is mounted, skipping.\n" if $VERBOSE >= 2;
next;
}
if (defined $match) {
next unless ($rec->[1] =~ m/$match/i or $rec->[2] =~ m/$match/i);
}
push @candidates, $rec->[0];
$entry .= ' '.$rec->[1];
$size += $rec->[3];
$cnt++;
}

# warn "# NOTE: got $cnt disk(s), default is 16.\n" if $cnt != 16;
# warn "# NOTE: label '$opts{n}' doesn't look good.\n" unless $opts{n} =~ m/^[CDM][\da-f]\d{3}$/;
if ($cnt != 16) {
my $msg = "got $cnt disk(s), default is 16.";
if ($cnt != $num_wanted) {
my $msg = "got $cnt disk(s), expected $num_wanted.";
$msg = $color->t_red($msg) unless $opts{m};
print "# NOTE: $msg\n";
}


if ($opts{n} !~ m/^[CDM][\da-f]\d{3}$/) {
my $msg = "label '$opts{n}' doesn't look good.";
my $msg = "label '$opts{n}' doesn't follow the CDM scheme.";
$msg = $color->t_red($msg) unless $opts{m};
print "# NOTE: $msg\n";
}


my $level=6;
my $chunk_size=512;
my $dev='md127';
if ($cnt) {
print "# Record for '$MDADMCONF_DB'\n";
if ($level == 0) {
# do nothing
} elsif ($level == 1) {
$size /= 2;
} elsif ($level == 5 and $cnt>1) {
$size = int ((($cnt-1)/$cnt) * $size);
} elsif ($level == 6 and $cnt>2) {
$size = int ((($cnt-2)/$cnt) * $size);
} else {
print "# RAID level $level not handled ($cnt disks)\n";
}
$size /= 1024**4; # TB


# 100TB -> 512k, 50TB -> 256k
while ( $size/$chunk_size < 0.115 and $chunk_size > 64) {
$chunk_size/=2;
}

while (-e "/dev/md$dev_no") {
$dev_no++;
die "# ERROR: out of devices ($dev_no). Stopped", if $dev_no >= 128;
}

my $dev='md'.$dev_no;
print "# Record for '$MDADMCONF_DB'\n\n";
print $entry;
print "\n\n";
print "# Hint for creation with mdadm:\n\n";
print "\n\n\n";
print "# Hint for creation with mdadm & mkfs.xfs:\n\n";
printf "mdadm -C /dev/%s -l %d -n %d -N %s -c %s %s\n\n",
$dev,
$level, $cnt,
$opts{n},
'0x0',
$chunk_size,
'/dev/' . join(' /dev/', @candidates);

my $msg = 'always check the chunk-size (-c)!';
printf "# HINT: %s (8TB:512/4TB:256/2TB:128)\n\n", $opts{m}?$msg:$color->t_red($msg);
printf "mkfs.xfs -L %s /dev/%s\n\n", lc($opts{n}), $dev;


printf "# Note: size of array will be %.1f TB\n\n", $size;
}
}

Expand Down Expand Up @@ -297,7 +338,7 @@ sub create_mdadm_configs {
$conf_dev_no=$dev_no;
$dev_no++;
}
push @mdadm_conf, [
push @mdadm_conf, [
$conf_dev_no , sprintf("ARRAY /dev/md%d devices=%s", $conf_dev_no, join ',', @tmp),
$rlabel , $condition_active, $condition_incomplete
]; # -2- -3- -4-
Expand Down Expand Up @@ -520,7 +561,10 @@ exit;
sub discover_basic {
my $self = shift;

my @disks = ( glob('/sys/block/sd[a-z]'), glob('/sys/block/sd[a-z][a-z]') ); # 676 disks max
# take only whole disks, no partitions
my @disks = grep {m:/(sd[a-z]+|nvme\d+n\d+)$:} (
glob('/sys/block/sd[a-z]*'), glob('/sys/block/nvme[0-9]*')
);
my $diskinfo = $self->get_hd_info(\@disks);
my @non_raid;
my %configured_raids;
Expand Down Expand Up @@ -647,36 +691,40 @@ exit;
}

# 'shortcut'
sub get_hd_info {
sub get_hd_serial {
my $self = shift;
$self->get_hd_serial($_[0], 1)
$self->get_hd_info($_[0], 1)
}

sub get_hd_serial {
sub get_hd_info {
my $self = shift;
my $dev_list = shift;
my $full_info = shift;
my $short_info = shift;

my @ret;
my $num = scalar @$dev_list;
for (my $i=0; $i<$num; $i++) {
my ($dk) = $dev_list->[$i] =~ m|([^/]+)$|;

my $model = sys_fs_get_prop('/sys/block/'.$dk.'/device/model');
my $sizeb = sys_fs_get_prop('/sys/block/'.$dk.'/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 $serial_number;
if (-e '/sys/block/'.$dk.'/device/vpd_pg80') {
if (-e '/sys/block/'.$dk.'/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'));
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');
$serial_number =~ s/^\s+//;
} else {
if ($self->{root_priv}) {
$serial_number = smartctl_info($dev_list->[$i], 'Serial_Number');
Expand All @@ -686,10 +734,10 @@ exit;
}

if (defined $serial_number) {
if ($full_info) {
push @ret, [$dk, $serial_number, $model];
} else {
if ($short_info) {
push @ret, $serial_number;
} else {
push @ret, [$dk, $serial_number, $model, $sizeb];
}
}
}
Expand Down