diff --git a/mxraid/mxraid_assemble b/mxraid/mxraid_assemble index 54bb440..529891b 100755 --- a/mxraid/mxraid_assemble +++ b/mxraid/mxraid_assemble @@ -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; } @@ -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]')); @@ -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}; @@ -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"; } @@ -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; } } @@ -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- @@ -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; @@ -647,15 +691,15 @@ 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; @@ -663,6 +707,7 @@ exit; 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}; @@ -670,13 +715,16 @@ exit; # } 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'); @@ -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]; } } }