This is a Pliant (http://pliant.cams.ehess.fr/) script that should enable
you to make changes in a RAID configuration with (or with the hope of)
no data loss.

Lets take an example:

The old /etc/raidtab configuration file is:

raiddev /dev/md0
    raid-level                5
    nr-raid-disks             3
    nr-spare-disks            0
    persistent-superblock     1
    chunk-size                64

    device                    /dev/hda5
    raid-disk                 0
    device                    /dev/hda6
    raid-disk                 1
    device                    /dev/hda7
    raid-disk                 2

The new one you want is:

raiddev /dev/md0
    raid-level                5
    nr-raid-disks             4
    nr-spare-disks            0
    persistent-superblock     1
    chunk-size                4

    device                    /dev/hda5
    raid-disk                 0
    device                    /dev/hda6
    raid-disk                 1
    device                    /dev/hda7
    raid-disk                 2
    device                    /dev/hda8
    raid-disk                 3

1) Run the following Pliant command: 
pliant module /sample/raidconvert.pli command 'raid_convert "/dev/md0" "/dev/hda5 
/dev/hda6 /dev/hda7" "/dev/hda5 /dev/hda6 /dev/hda7 /dev/hda8" 5 4*1024'
- pameter 2 of 'raid_convert' is the list of the raid devices
  in the old RAID configuration (sparce disks should not be listed)
- pameter 3 is the list of the raid devices
  in the new RAID configuration (sparce disks should not be listed)
- pameter 4 is the raid level in the new RAID configuration 
- pameter 5 is the new chunk size

2) Modify your /etc/raidtab

3) use 'mkraid' command in order to recreate the new RAID array
   (your data should be preserved)

This script should also enables you to remove some disks, to change the
raid level or the chunk size, BUT TEST IT ON SAMPLES BEFORE APPLYING
TO ANY SERIOUS DATAS.

You must use 'ext2resize' command BEFORE 1 if your new RAID array will be
smaller than the old one, or after 3 if it's bigger.

You should also be awared that if anything wrong appends during the
conversion (any io error), the program will abort ungracefully and
all datas will be lost.

Good luck brave peoples
Hubert Tonneau


# Copyright (C) 1999  Hubert Tonneau  [EMAIL PROTECTED]
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# version 2 along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

# release 1 for Pliant release 25

module "/pliant/v1.pli"
module "/pliant/meta.pli"

constant sector_size 512

function os_llseek handle high low result whence -> err
  arg Int handle ; arg uInt high low ; arg Address result ; arg Int whence ; arg Int 
err
  kernel_function 140
  

function pgcd a b -> g
  arg Intn a b g
  var Intn x := a
  var Intn y := b
  g := y
  while x>0
    g := x
    x := y%x
    y := g

function ppcm a b -> m
  arg Intn a b m
  m := a*b\(pgcd a b)

function min a b -> c
  arg Intn a b c
  c := shunt a<=b a b


type Device
  field Int handle <- -1
  field Str name

method d open device_name
  arg_w Device d ; arg Str device_name
  d name := device_name
  var Str namez := device_name+"[0]"
  d handle := os_open namez:characters 2 0
  if d:handle<0
    error "Failed to open device "+d:name

method d close
  arg_w Device d 
  if (os_close d:handle)<>0
    error "Failed to open device "+d:name
  d handle := -1

method d seek position
  arg Device d ; arg Intn position
  if position%sector_size<>0
    error "Missaligned seek applyed to device "+d:name+" ("+(cast position Str)+")"
  var uInt high := cast position\(cast 2 Intn)^32 uInt
  var uInt low := cast position%(cast 2 Intn)^32 uInt
  check high*(cast 2 Intn)^32+low=position
  if (os_llseek d:handle high low addressof:(var uInt64 result) 0)<>0
    error "Failed to set position for device "+d:name

method d read buffer size
  arg Device d ; arg Address buffer ; arg Int size
  var Int red := os_read d:handle buffer size
  if red<>size
    error "Failed to read from device "+d:name

method d write buffer size -> status
  arg Device d ; arg Address buffer ; arg Int size ; arg Status status
  var Int written := os_write d:handle buffer size
  if written<>size
    error "Failed to write to device "+d:name


type Raid
  field Str device_name
  field Array:Device devices
  field Int level
  field Int chunk_size
  field Intn size
 
method raid define device_name devices_names
  arg_rw Raid raid ; arg Str device_name devices_names
  raid:device_name := device_name
  raid:devices size := 0
  var Str names := devices_names
  while names<>""
    if (names match (var Str name1) _ (var Str name2))
      (var Device d) open name1
      raid:devices += d
      names := name2
    else
      (var Device d) open names
      raid:devices += d
      names := ""

method raid data_disks_count -> count
  arg Raid raid ; arg Int count
  if raid:level=0
    count := raid:devices:size
  eif raid:level=1
    count := 1
  eif raid:level=5
    count := raid:devices:size-1
  else
    error "raid level "+(cast raid:level Str)+" is not supported yet."
  
method raid query
  arg_rw Raid raid 
  (var Stream s) open "/proc/mdstat" in
  while not s:atend
    if (s:readline match (var Str name) ":" word:"active" "raid" (var Int level) (var 
Str stat))
      if "/dev/"+name=raid:device_name
        var Int disks_count := 0
        while (stat match (var Str drop) "[[]" (var Int i) "[]]" _ (var Str stat2)) 
and (drop search " " -1)=(-1)
          disks_count += 1
          stat := stat2
        if disks_count<>raid:devices:size
          error "The number of disks in "+raid:device_name+" does not match your 
declarations."
        var CBool ok := false
        if level=0 and (stat match (var Int blocks) word:"blocks" (var Int chunk) "k" 
word:"chunks")
          raid chunk_size := chunk*1024
          ok := true
        eif level=1 and (stat match (var Int blocks) word:"blocks"  "[[]" (var Str 
drop) "[]]" "[[]" (var Str ups) "[]]")
          raid chunk_size := 512
          ok := true
        eif level=5 and (stat match (var Int blocks) word:"blocks" word:"level" (var 
Int level) "," (var Int chunk) "k" word:"chunk" "," word:"algorithm" (var Int 
algorithm) "[[]" (var Str drop) "[]]" "[[]" (var Str ups) "[]]")
          raid chunk_size := chunk*1024
          ok := algorithm=0
        if ok
          raid level := level
          raid size := (cast 1024 Intn)*blocks
          if raid:size%(raid:chunk_size*raid:data_disks_count)<>0
            error "The total "+raid:device_name+" size does not match the chunk size 
and number of devices"
          return              
  error "Unexpected "+raid:device_name+" status !"

method raid map raid_offset map_parity device device_offset -> continous_size
  arg Raid raid ; arg Intn raid_offset ; arg CBool map_parity ; arg_w Device device ; 
arg_w Intn device_offset ; arg Int continous_size
  if raid_offset<0 or raid_offset>=raid:size
    error "Attemted to access raid device outside boundaries"
  if raid:level=0
    var Intn chunk_number := raid_offset\raid:chunk_size
    continous_size := (chunk_number+1)*raid:chunk_size-raid_offset
    var Int chunk_per_row := raid:devices:size
    var Intn row := chunk_number\chunk_per_row
    var Int col := chunk_number%chunk_per_row
    device := raid:devices col
    device_offset := row*raid:chunk_size
  eif raid:level=1
    device := raid:devices 0
    device_offset := raid_offset
    continous_size := min raid:size-raid_offset 64*2^10
  eif raid:level=5
    var Intn chunk_number := raid_offset\raid:chunk_size
    continous_size := (chunk_number+1)*raid:chunk_size-raid_offset
    var Int chunk_per_row := raid:devices:size-1
    var Intn row := chunk_number\chunk_per_row
    var Int col := chunk_number%chunk_per_row
    var Int parity := raid:devices:size-1-row%raid:devices:size
    if col>=parity
      col += 1
    if map_parity
      col := parity
    device := raid:devices col
    device_offset := row*raid:chunk_size
  else
    error "raid level "+(cast raid:level Str)+" is not supported yet."

method raid read offset parity buffer size
  arg Raid raid ; arg Intn offset ; arg CBool parity ; arg Address buffer ; arg Int 
size
  var Int done := 0
  while done<size
    var Int step := min (raid map offset+done parity (var Device d) (var Intn o)) 
size-done
    d seek o ?
    d read (buffer translate Byte done) step ?
    done += step
  
method raid write offset parity buffer size
  arg Raid raid ; arg Intn offset ; arg CBool parity ; arg Address buffer ; arg Int 
size
  var Int done := 0
  while done<size
    var Int step := min (raid map offset+done parity (var Device d) (var Intn o)) 
size-done
    d seek o ?
    d write (buffer translate Byte done) step ?
    if raid:level=1
      for (var Int i) 1 raid:devices:size-1
        raid:devices:i write (buffer translate Byte done) step ?
    done += step
  

function memory_xor buffer1 buffer2 size
  arg Address buffer1 buffer2 ; arg Int size
  for (var Int i) 0 size step Int:size
    (buffer1 translate Byte i) map Int := ((buffer1 translate Byte i) map Int) .xor. 
((buffer2 translate Byte i) map Int)


function raid_convert raid_device old_devices new_devices new_raid_level new_chunk_size
  arg Str raid_device old_devices new_devices ; arg Int new_raid_level new_chunk_size
  (var Raid old) define raid_device old_devices
  (var Raid new) define raid_device new_devices
  shell "umount "+raid_device+" 2>/dev/null"
  shell "raidstop "+raid_device+" 2>/dev/null"
  if (shell "raidstart "+raid_device)<>0
    error "Failed to start RAID array "+raid_device ?
  old:query ?
  console "[lf]The old RAID" old:level " array is a " old:size\2^20 " MB RAID" 
old:level " array on " old:devices:size " disks and " old:chunk_size\1024 " K 
chunks.[lf]"
  var Int old_unit := old:chunk_size*old:data_disks_count
  new level := new_raid_level
  new chunk_size := new_chunk_size
  new size := old:size*new:data_disks_count\old:data_disks_count
  console "The new RAID" new:level " array will be a " new:size\2^20 " MB RAID" 
new:level " array on " new:devices:size " disks and " new:chunk_size\1024 " K 
chunks.[lf]"
  var Int new_unit := new:chunk_size*new:data_disks_count
  os_sysinfo (var os_SysInfo sys)
  var Int memory_physical := sys totalram
  var Int common_unit := ppcm old_unit new_unit
  while common_unit<64*2^10
    common_unit*=2
  # console "Conversion step will be " common_unit\2^10 " KB  (physical memory is " 
memory_physical\2^20 " MB)[lf]"
  if common_unit>4*2^20 and common_unit>memory_physical\2
    error "The conversion would consume too much memory" ?
  console "[lf]THIS IS VERY ALPHA CODE, IT MAY DESTROY ALL YOUR DATAS ![lf]"
  console "You have 10 seconds to press Ctrl+C if you changed your mind.[lf]"
  sleep 10
  console "So, let's go.[lf][lf]"
  if true
    console "step 1: checking consistency in the existing array[lf]"
    console "It is still safe to stop while step 1 is running.[lf]"
    (var Device raid) open raid_device
    var Address md := memory_allocate old_unit null
    var Address chunks := memory_allocate old_unit null
    var Address checksum1 := memory_allocate old:chunk_size null
    var Address checksum2 := memory_allocate old:chunk_size null
    var Intn position := 0
    while position<old:size
      if position\2^20<>(position-old_unit)\2^20
        console "checked " position\2^20 " MB (" 100*position\old:size "%)[cr]"
      raid seek position ?
      raid read md old_unit
      old read position false chunks old_unit
      if (memory_compare md old_unit chunks old_unit)<>compare_equal
        error "This program is buggy !" ?
      if old:level=5
        memory_clear checksum1 old:chunk_size
        for (var Int i) 0 old_unit-1 step old:chunk_size
          memory_xor checksum1 (chunks translate Byte i) old:chunk_size
        old read position true checksum2 old:chunk_size
        if (memory_compare checksum1 old:chunk_size checksum2 
old:chunk_size)<>compare_equal
          error "The raid array is corrupted !" ?
      position += old_unit
    memory_free md
    memory_free chunks
    memory_free checksum1
    memory_free checksum2
    raid close
  if (shell "raidstop "+raid_device)<>0
    error "Failed to stop RAID array "+raid_device ?
  if true
    console "step 2: moving the datas around[lf]" ?
    console "IF THIS PROCESS IS STOPPED IN THE MIDDLE, YOUR DATAS WILL BE LOST.[lf]"
    sleep 2
    var Address buffer := memory_allocate common_unit null
    var Intn size := min old:size new:size
    var CBool forward := new:data_disks_count>=old:data_disks_count
    var Intn position := shunt forward 0 (size-1)\common_unit*common_unit
    while (shunt forward position<size position>=0)
      console "moved " (shunt forward position size-position)\2^20 " MB (" 100*(shunt 
forward position size-position)\size "%)[cr]"
      var Int step := min size-position common_unit
      old read position false buffer step
      new write position false buffer step
      position += shunt forward common_unit -common_unit
    memory_free buffer
    console "The conversion is done.[lf]"
    console "Please update your /dev/raidtab file accordingly[lf]"
    console "then use 'mkraid --force "+raid_device+"' command[lf]"
    console "in order to update the raid superblocks"+(shunt new:level>0 " and resync" 
"")+".[lf]"
    
  
export raid_convert

Reply via email to