2011. október 9., vasárnap

AIX mounting issue

Kicsit régebbi eset(2009.08), de gondoltam inkább lebloggolom, mintsem az enyészeté legyen, mert annál azért többet ér :)

Nos hát.. Alapvető probléma a következő: A rendszer indulásakor egy csomó filerendszer nem jön fel normálisan, holott a /etc/filesystems alatt szépen be van állítva, hogy azoknak bizony fel kell jönniük. A Volume Group szépen aktiválódik, de az alatta lévő FS-ek a rendszer indulásakor nem jönnek fel (Ha manuálisan mountoljuk őket, akkor viszont igen)

Kiegészítő információk:
- Az AIX kicsit régi a gépen (5300-10-01-0921) de ez nem indokolja a rendszer hibáját.
- A bootolás után a console log ilyen randaságokat tartalmaz:
0 Sun Aug 16 16:43:35 BST 2009 mount: 0506-324 Cannot mount /dev/lv_ap02n8log on /vfs=jfs2log=/dev/loglv27: A file or directory in the path name does not exist.
0 Sun Aug 16 16:43:35 BST 2009 mount: 0506-324 Cannot mount /dev/lv_ap02n2temp01 on /dev=/dev/lv_ap02n2data02log=/dev/loglv20: A file or directory in the path name does not exist.
0 Sun Aug 16 16:43:35 BST 2009 mount: 0506-324 Cannot mount /dev/lv_ap02n3data02 on /dev=/dev/lv_ap02n2temp02log=/dev/loglv20check=false: A file or directory in the path name does not exist.
0 Sun Aug 16 16:43:35 BST 2009 mount: 0506-324 Cannot mount /dev/lv_ap03n9log on /vfs=jfs2: A file or directory in the path name does not exist.

Mint az látszik a console logból a rendszer össze vissza kavarva próbál valami mount félét össze hozni, de nagyon kevés sikerrel.
Na de a kérdés.. Miért, és hogy?
Ehhez első körben vizsgáljuk meg, hogy mi (és hogy) is lenne a felelős ennek folyamatnak a lezongorázásáért:

A folyamat alapvetően egyszerű: a rendszer IPL-je során amint átvált a rendszer rc2-be nyomban elkezdi a /etc/inittab-ot feldolgozni, illetve az ott feltüntetett parancsokat/scripteket végrehajtani. Az egyik ilyen alap script a /etc/rc, ami a mountolásért is felel. Az akkor rendszeren az alábbi kód volt ezért a felelős (megjegyzés - a kód verziószáma 1.20.1.7 - ez még később fontos lesz)

# Perform file system checks
# The -f flag skips the check if the log has been replayed successfully
fsck -fp

# Perform all auto mounts
dspmsg rc.cat 4 ' Performing all automatic mounts \n'

cat /etc/filesystems | tr -d ' ' | tr -d '\t' | grep -vp "vfs=nfs" | grep -vp "vfs=cachefs"> /tmp/fs1.$$

# Collecting entries except nfs
lines=`wc -l /tmp/fs1.$$ | awk '{print $1}'`

# Calculate the number of lines
cnt=1

# Remove the file 'fs2.$$' if it already exists
if [ -f /tmp/fs2.$$ ]
then
        rm -f /tmp/fs2.$$
fi

# Compare each line in the file '/tmp/fs1.$$'
# If it is a comment line or just having the filesystem name,
# just write it to '/tmp/fs2.$$'
# Otherwise, add a tab before that line and write it to '/tmp/fs2.$$'

while [ $cnt -le $lines ]
do
        cat /tmp/fs1.$$ | head -$cnt | tail -1 | grep "^[\*/]"  1> /dev/null
        if [ $? -eq 0 ]
        then
                cat /tmp/fs1.$$ | head -$cnt | tail -1 >> /tmp/fs2.$$
        else
                param=`cat /tmp/fs1.$$ | head -$cnt | tail -1`
                echo "\t"$param >> /tmp/fs2.$$
        fi
        cnt=`expr $cnt + 1`
done
mount /tmp/fs2.$$ /etc/filesystems
mount all
umount /etc/filesystems
rm -f /tmp/fs1.$$ /tmp/fs2.$$

Nah.. mit is látunk itt...
- Első körben a rendszer létrehoz egy /tmp/fs1.$$ file-t ($$ a PID szám, amivel maga a /etc/rc fut), amit aztán majd alapnak fog használni. A file-ból minden tab és szóköz hiányzik, illetve a cachefs-eket is kikapja belőle a grep -vp.
- Aztán egy $lines változót használva a rendszer megnézi, hogy az így keletkezett file hány soros (bár sztem kicsit undorítóan csinálja, de hát ízlés kérdése).
- Mindezek után egy counter-t használva ($lines értékéig) egy teljes while ciklus indul, ami egy (szerintem szintén undorító módon) sorról-sorra végigmegy, és megnézi, hogy az adott sor '*'-al, avagy '/'-el kezdődik e (grep visszatérési értéke). Amennyiben igen, úgy az adott sor átkerül egy /tmp/fs2.$$ file-ba. Amennyiben mégse így történne akkor is, csak az adott sor kap maga elé egy tabulátort.
- Amint a ciklus lefut egy meglehetősen furcsa módszerrel a /tmp/fs2.$$-ban lévő FS-eket mountolja a rendszer (figyelem - a /etc/filesystems helyére felülmountolódik a /tmp/fs2.$$ ), majd a standard mount all parancsal minden a file-ban szereplő FS mountolódik (aminek a mount paramétere nem 'false').
- Amint ezzel is kész feloldja az előző mountot, majd törli a temporary file-okat.

A módszer kicsit tényleg furcsán néz ki (nekem főleg a sorról-sorra végigmenős módszertől borsódzik a hátam), viszont elméletben működnie kellett volna.. Akkor hol volt a probléma?? Nos.. végeztem pár tesztet, majd a szemem fennakadt, amikor nem az általam várt eredmények jöttek ki:

A használt script a következő volt:
#!/bin/ksh
set -x
lines=`wc -l /tmp/fs1.temp | awk '{print $1}'`
cnt=1

while [ $cnt -le $lines ]
do
        cat /tmp/fs1.temp | head -$cnt | tail -1 | grep "^[\*/]"  1> /dev/null
        if [ $? -eq 0 ]
        then
                cat /tmp/fs1.temp | head -$cnt | tail -1 >> /tmp/fs2.temp
        else
                param=`cat /tmp/fs1.temp | head -$cnt | tail -1`
                echo "\t"$param >> /tmp/fs2.temp
        fi
        cnt=`expr $cnt + 1`
done
Lényegében azonos a standard scriptel, az eredmény (/tmp/fs2.temp) viszont nem az volt amit vártam:
/var:
dev=/dev/hd9var
vfs=jfs2
    log=/dev/hd8
    mount=automatic
check=false
type=bootfs
    vol=/var
    free=false
quota=no

/tmp:
    dev=/dev/hd3
    vfs=jfs2
log=/dev/hd8
mount=automatic
check=false
vol=/tmp
free=false
quota=no

/proc:
    dev=/proc
    vol="/proc"
    mount=true
    check=false
    free=false
    vfs=procfs
Mit látható.. A TAB-ok egy csomó helyen nem kerültek a helyükre.. Na akkor nézzük meg azt az elágazást kicsit közelebbről:
cat /tmp/fs1.temp | head -$cnt | tail -1 | grep "^[\*/]"  1> /dev/null
if [ $? -eq 0 ]
then
        cat /tmp/fs1.temp | head -$cnt | tail -1 >> /tmp/fs2.temp
else
        param=`cat /tmp/fs1.temp | head -$cnt | tail -1`
        echo "\t"$param >> /tmp/fs2.temp
fi
Tehát.. Ha a $? (előző parancs (grep) Return Code-ja) 0 akkor csak rakja bele, ha meg nem akkor toldja meg egy tabbal.. Hát jó. csináljunk egy kis tesztet:
server:root # while true;do cat /tmp/fs1.temp | head -172| tail -1 | grep "^[\*/]"  1>/dev/null;echo $?;done
1
2
13
13
13
13
13
13
És kb itt nyomtam Ctrl+C-t..

Mit is csináltam? - egy végelenített ciklust, amikor is folyamatosan ugyan azt az adatot kértem ki (head+tail) majd megnéztem a grep return code-ját.. Az viszont azonos adat esetén (172. sor) különböző eredményt produkált! Wattafák???

Nos.. Mint kiderült sikerült belefutni egy meglehetősen érdekes issue-ba. Ennek az issue-nak neve is volt: IZ43276: KSH MAY RETURN INCORRECT RETURN CODE FOR PIPED COMMANDS APPLIES TO AIX 5300-11

Aha... Hogy úgy... Én is üdvözöllek.. A problémámon ez a tudás akkor viszont még nem segített.. A patch-et csak 2010ben adták ki, ami azt jelentette, hogy én 2009ben még csak feliratkozhattam a fix "hírlevél mondóra" és várhattam, hogy javítják a hibát..

Viszont mint kiderült azért volt kerülőút: Az AIX 5.3 TL6-tól (TL10-en van a rendszer, emlékeztek? :) a /etc/rc file már a 1.20.1.9 verzión volt, ami ugyen ezt a problémát (FS-ek mountolása) sokkal szebben oldotta meg:

# Remove the file 'fs1.$$' if it already exists
rm -f /tmp/fs1.$$

# handle the egrep line carefully: between each pair of brackets is a tab
# followed by a space, and the tab may get lost if you copy and paste the line
egrep -vp "^[ ]*vfs[ ]*=[ ]*(cachefs|nfs|cifs)[ ]*$" \
/etc/filesystems > /tmp/fs1.$$

mount /tmp/fs1.$$ /etc/filesystems
mount all
umount /etc/filesystems
rm -f /tmp/fs1.$$

Éljenek a Reguláris kifejezések.. Ennek a kódnak köszönhetően pedig (mivel nem használ hülye visszatérési értékeket) a mount problémám megoldódott (persze a rendszeren lévő további scriptek amik erre támaszkodtak ettől még szívhatták).

Egy kérdés maradt csak nyitva - Ha ez TL6tól elérhető volt, akkor miért nem ez a verzió volt elérhető a TL10-es AIX alatt is?

A válasz persze "prózai" - A fejlesztők szerint a /etc/rc file-t a customer (ha nem is nyugodtan, de.. ) bármikor szerkesztheti, így úgy gondolták, hogy az upgrade során ezt a file-t inkább ne piszkálják (hisz új funkciót amúgy se hoz) upgrade során. Ergo ha a rendszer egy régebbi szintről ( < TL6 ) lett felhúzva TL10-re, akkor az upgrade véletlenül se fogja a frissebb file-t a helyére pakolni. Max akkor ha a rendszert már az elejétől fogva TL6, vagy afölötti verzióval telepítették (ergo az újabb /etc/rc már alapból része a telepítő csomagnak)..

Igen.. Én is örültem amikor megtudtam :)

2 megjegyzés:

  1. Most épp megint unatkozom, így újraolvastam ezt a cikket.

    Amit nem értek: sok csomagkezelö (például az RPM is) azt csinálja, hogy a csomagban levö konfig fájl checksumját összeveti a felülvágni kívanttal, és ha nem egyezik a sztori, akkor a telepitödö konfig fájlt egy extra kiterjesztéssel (.rpmnew pl.) rakja fel. Viszont, ha a checksum nem talál eltérést, akkor felülvág. Ke'kszcsokolom, ezt a AIX csomagkezelöje miért nem tudja? Tippre ott is van checksum a csomagokban a fájlokhoz (muszáj).

    VálaszTörlés
  2. Ebben részben igazad van ,de azért ne mértek teljesen egyet:
    - Egy részről sok féle verzióról lehet frissíteni az AIX-et az adott verzióra (és itt most ne csak az 5.3-akat vedd, hanem akár migrálást 5.2-ről is). Ez persze még mindig nem teszi lehetetlenné, hogy kb 20-30 checksummal összevessük a file-t és az alapján cselekedjünk, de jóval macerásabbá teszi a dolgot (még úgy is, hogy ezt simán meg lehetne csinálni pre/post install scriptekkel).
    - Más részről viszont a /etc/rc file szerepe jó ideje nem változott semmit, így inkább azt csak folyamatosan polírozták a fejlesztők. Mindemellett persze megmaradt az, hogy a customerek ezt attól még lelki nyugalommal piszkálhatják (a saját felelősségükre, bár ezzel semmit nem sértenek, ergo a supportot nem vesztik el vele), mert ki tudja melyiknek milyen sötét gondolata támad, ami miatt aztán ezt a file-t átírják. Ergo az upgrade során a customer által beleírt dolgot nem ártana megtartani, ami viszont jóvalta macerásabbá teszi az egész megoldást.
    - Esetleg az valóban megoldás lehetne, hogy a régi file-t lebackupolják, elmentik egy ${FILE}.old néven, aztán a jelenlegit felülvágják, illetve install közben közli a telepítő, hogy "ha volt ott valamid, akkor készülj, mert vissza kell rakd", de sok esetben az ember annyi mindent telepít egyszerre (egy migrálás pl simán fel tud kúszni 1-2e csomagig), hogy ez az üzenet olyan gyorsan eltűnne, hogy az ember észre se venné. Ergo ez se annyira egyszerű eset.

    Így személy szerint azt mondom, hogy a fejlesztők döntése valamilyen szinten szerintem érthető, mivel egyszerűen nem éri meg olyan mélységig belemenni az install közben a checkekbe, hogy minden lehetőséget lefedjél.

    btw - Ha nagyon unatkozol, akkor készülj, mert ehétvégén még tervezek feltenni egy postot, némileg hasonló témakörben (bug hunting + fejlesztők)

    VálaszTörlés