#!/usr/bin/perl # This script reads lines from STDIN, each line # representing read speed of one PV extent, # then produces lvcreate command marking slow areas. use strict; use warnings; # Configuration variables my $min_speed = 60; # Minimum allowed read speed my $trim_edges = 3; # Trim this many extents from each side of good areas my $min_good_size = 125; # Good areas below this number of extents are marked as bad # Only used to produce lvcreate command my $vg = 'vg_name'; my $pv = 'pv_name'; my $lv_name = 'bad'; # Converts array of values to RLE representation sub rle { return () if scalar @_ == 0; my $v; my $l = 0; my $t; my @rle = map { if($l == 0) { $l++; $v = $_; (); } elsif ($v == $_) { $l++; (); } else { $t = [$v, $l]; $l = 1; $v = $_; $t; } } @_; push @rle, [$v, $l]; return @rle; } # Process array of speed values sub process_data { my @data = @_; # Values less than $min_speed are changed to 0, greater or equal to 1 @data = map { $_ >= $min_speed ? 1 : 0 } @data; # Trim good areas by calculating logical AND of each data point with its neighbours for(1..$trim_edges) { @data = ($data[0] & $data[1], (map { $data[$_-1] & $data[$_] & $data[$_+1] } 1..$#data-1), $data[$#data-1] & $data[$#data]); } return @data; } # Read data my @data; while() { my($extent, $speed) = split / /; push @data, $speed; } # Apply $min_speed threshold (0 = below = bad, 1 = above or equal = good), and trim @data = process_data(@data); # Change to LRE... my @rle = rle(@data); # ...and change areas smaller than $min_good_size to 0 (bad) @rle = rle(map { my ($v, $l) = @$_; $l < $min_good_size ? map {0} 1..$l : map{$v} 1..$l } @rle); # Collect bad extents my %sum; my @pv_extents; my $start = 0; for(@rle) { my ($v, $l) = @$_; my $n = ($v == 0) ? 'bad' : 'good'; $sum{$n} += $l; my $end = $start + $l - 1; push @pv_extents, "$pv:$start-$end" if $v == 0; $start += $l; } # And print lvcreate command print "lvcreate -n $lv_name -l $sum{bad} $vg @pv_extents\n"; print "# $sum{good} $sum{bad}\n";