A következő címkéjű bejegyzések mutatása: mount. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: mount. Összes bejegyzés megjelenítése

2014. szeptember 28., vasárnap

WTF - Megváltozott filerendszer mount pointok migrálás után

Aki már migrált valaha bármilyen régebbi rendszert bármilyen újra, az kb tudhatja hogy változatosabbnál változatosabb csapdákba futhat bele az ember időnként. Ez most se volt másként...

Adott egy régi Power4es szerver amin épp egy 5.2es AIX futkározik. Tekintve hogy ez a HW már jó ideje nem támogatott, sok helyet is foglal, meg amúgy is lassú, így fent az a döntés született, hogy konszolidálni kéne a szervert egy modernebb Power7es infrastruktúrába.
Hogy hogyan is? Hát van több fajta megoldás, csak tudni kell kiválasztani melyik a legcélravezetőbb:
- Live Partition Mobility: Ha a forrás és a cél rendszerek mind szépen virtualizálva vannak, és a szükséges feltételek mind adottak, akkor kiesés nélkül át lehet költöztetni az LPARt 2 fizikai rendszer között
- Lift and Shift: Ha a rendszer és az adatok is külső SAN-on vannak, akkor minimális leállás után át lehet zónázni a LUN-okat egy másik gépre és ott használni őket.
- Backup and restore: Ez esetben minimum a rendszer belsős lemezekre lett telepítve, így annak költöztetése csak egy mksysb backup segítségével megoldható, amit a távoli gépen aztán vissza lehet tölteni (akár WPAR-ba is). Ha mázlink van, akkor az adatok SAN-on helyezkednek el, így a specifikus LUN-okat át lehet simán zónázni a cél géphez; ha viszont nincs mázlink, akkor ezt is hálózaton keresztül valahogy át kell juttatni a cél gép felé (savevg, GLVM, vagy sima tar+ssh pipe)
- Rebuild from scratch: Ebben az esetben az alap rendszert (illetve opcionálisan a Middleware, Applikációt is) teljesen nulláról építjük újra, majd a szolgáltatáshoz szükséges további adatot átemeljük a forrás rendszerről és beillesztjük az újonnan felépített struktúrába.

Lehet meglepő, de régebbi rendszereknél az utóbbi sokszor a legcélravezetőbb, mivel ezeknél valószínű nem csak a rendszer régi, hanem annak komponensei is, így egy-egy ilyen migráció kiváló alkalmat ad arra, hogy az ember végre kidobálja a régi legacy szutykokat, és egy támogatott megoldással helyettesítse őket.

Ebben az esetben a legcélravezetőbbnek az utolsó 2 megoldás keveréke bizonyult a legjobbnak: Az alaprendszert nem volt értelme átköltöztetni, a régi -már nem támogatott- middleware komponenseket egy részét szerencsére volt lehetőség támogatottra cserélni, más komponenseket viszont simán át lehetett emelni a régi rendszerről a szükséges LUN-ok újrazónázásával.

A tényleges lépésekre most nem térek ki, -a bejegyzés szempontjából érdektelenek- egyet kivéve: Miután a LUN-okat az ember átzónázza Volume Groupot ismét be kell importálni a rendszerbe, hogy az adathoz hozzá lehessen férni. A folyamat részeként a teljes LVM struktúra importálódik a fájlrendszerekkel egyetemben. Igen ám, de volt egy kisebb probléma.. Az importálás után a legtöbb fájlrendszer szépen vissza is került a helyére, viszont volt 1-2 aminek a mount pointja megváltozott (ettől eltekintve az adat szépen a helyén volt). A kérdés adott volt: Miért?

No, a technikai része a blog bejegyzésnek kb. innen indul :)) Lássuk mi és hogy zajlik le egy ilyen importálás során:
- Az importálás során az importvg szépen felolvassa a megjelölt diszk VGDA adatait
- A VGDA adatok alapján ellenőrzi a VG-hez szükséges PV-k elérhetőségét (PVID alapján), illetve összeveti azok VGDA adatait (VG_ID) a többi PV-n találhatóakkal.
- Amennyiben az összes PV elérhető, és a VGDA is konzisztensen azonos adatot mutat, úgy felolvassa a teljes LVM struktúrát
- A struktúra része az adott LV-k definiciói (típus, név, méret, upper bound), azok elhelyezkedése (LP/PP-k elhelyezkedése PV-re lebontva), illetve 1-2 további metaadat (létrehozási idő, utolsó módosítási idő, label, vfs specifikus adatok)
- Miután a teljes struktúrát feltérképezte létrehozza a szükséges bejegyzéseket az ODM-ben, illetve a /etc/filesystems-ben, majd ezek alapján aktiválja a VG-t

Nem tudom kinek tűnik fel, de fájlrendszer specifikus adatokat az importvg nem olvas fel, még is módosítja a /etc/filesystems file-t.. Hogy van ez?

Na igen. Itt van a kutya elásva. Igaz, hogy FS specifikus adatot nem olvas fel, viszont nem is kell neki: A metaadatokban ott van minden amire szüksége van ahhoz, hogy egy fájlrendszert mountolni lehessen.

Itt egy pelda a /home FS (hd1 LV) LVM descriptorának elejéről:

3D8220000 41495820 4C564342 00006A66 73320000 |AIX LVCB..jfs2..| # LVM Control Block, LV type 3D8220010 00000000 00000000 00000000 00000000 |................| 3D8220020 00000000 00000000 00003030 63306164 |..........00c0ad| # LV serial ID 3D8220030 31653030 30303463 30303030 30303031 |1e00004c00000001| 3D8220040 34320068 64310000 00000000 00000000 |42.hd1..........| 3D8220050 00000000 00000000 00000000 00000000 |................| 3D8220060 00000000 00000000 00000000 00000000 |................| 3D8220070 00000000 00000000 00000000 00000000 |................| 3D8220080 00000053 756E204E 6F762031 30203231 |...Sun Nov 10 21| # Creation date 3D8220090 3A30393A 34382032 3031330A 00000000 |:09:48 2013.....| 3D82200A0 00576564 204A756C 20323320 30393A31 |.Wed Jul 23 09:1| # Last modification date 3D82200B0 353A3338 20323031 340A0000 00000030 |5:38 2014......0| 3D82200C0 41443145 34433030 00796D63 00790020 |AD1E4C00.ymc.y. | 3D82200D0 00010001 2F686F6D 65000000 00000000 |..../home.......| # Label 3D82200E0 00000000 00000000 00000000 00000000 |................| 3D82200F0 00000000 00000000 00000000 00000000 |................| 3D8220100 00000000 00000000 00000000 00000000 |................| 3D8220110 00000000 00000000 00000000 00000000 |................| 3D8220120 00000000 00000000 00000000 00000000 |................| 3D8220130 00000000 00000000 00000000 00000000 |................| 3D8220140 00000000 00000000 00000000 00000000 |................| 3D8220150 00000000 7666733D 6A667332 3A6C6F67 |....vfs=jfs2:log| # VFS data 3D8220160 3D2F6465 762F6864 383A6D6F 756E743D |=/dev/hd8:mount=| 3D8220170 74727565 3A636865 636B3D74 7275653A |true:check=true:| 3D8220180 766F6C3D 2F686F6D 653A6672 65653D66 |vol=/home:free=f| 3D8220190 616C7365 3A71756F 74613D75 73657271 |alse:quota=userq| 3D82201A0 756F7461 2C67726F 75707175 6F746100 |uota,groupquota.|

Mielőtt valaki nekem ugrik, hogy de ezek fájlrendszer adatok: Nem, az 8000(h) blokkal arrébb van:

3D8228000 4A324653 00000002 00000000 527FF623 |J2FS........R..#| 3D8228010 00000000 00000000 00000000 00002F68 |............../h| 3D8228020 6F6D6500 00000000 00000000 00000000 |ome.............|

Jó. De ha ott megtalálható az adat, akkor miért nem az alapján dolgozik az importvg? A válasz erre nagyon egyszerű: Mert ott az se a mount point :)

De akkor várjunk csak egy pillanatot.. Itt van 2 olyan példa is, ahol egyértelműen látszik, hogy ott van a /home, de ez mégse a mount point? Hülyén hangzik, mi? :)) Nézzünk egy másik példát, és akkor már talán tisztább lesz. Legyen a példa a /var/adm/ras/livedump filerendszer (livedump LV):

LVM descriptor eleje:

1BC220000 41495820 4C564342 00006A66 73320000 |AIX LVCB..jfs2..| # LVM Control Block, LV type 1BC220010 00000000 00000000 00000000 00000000 |................| 1BC220020 00000000 00000000 00003030 63306164 |..........00c0ad| # LV serial ID 1BC220030 31653030 30303463 30303030 30303031 |1e00004c00000001| 1BC220040 3432006C 69766564 756D7000 00000000 |42.livedump.....| 1BC220050 00000000 00000000 00000000 00000000 |................| 1BC220060 00000000 00000000 00000000 00000000 |................| 1BC220070 00000000 00000000 00000000 00000000 |................| 1BC220080 00000053 756E204E 6F762031 30203231 |...Sun Nov 10 21| # Creation date 1BC220090 3A30393A 35302032 3031330A 00000000 |:09:50 2013.....| 1BC2200A0 0053756E 204E6F76 20313020 32313A31 |.Sun Nov 10 21:1| # Last modification date 1BC2200B0 313A3530 20323031 330A0000 00000030 |1:50 2013......0| 1BC2200C0 41443145 34433030 00796D6D 00790020 |AD1E4C00.ymm.y. | 1BC2200D0 00040001 2F766172 2F61646D 2F726173 |..../var/adm/ras| # Label 1BC2200E0 2F6C6976 6564756D 70000000 00000000 |/livedump.......| 1BC2200F0 00000000 00000000 00000000 00000000 |................| 1BC220100 00000000 00000000 00000000 00000000 |................| 1BC220110 00000000 00000000 00000000 00000000 |................| 1BC220120 00000000 00000000 00000000 00000000 |................| 1BC220130 00000000 00000000 00000000 00000000 |................| 1BC220140 00000000 00000000 00000000 00000000 |................| 1BC220150 00000000 7666733D 6A667332 3A6C6F67 |....vfs=jfs2:log| # VFS data 1BC220160 3D2F6465 762F6864 383A6D6F 756E743D |=/dev/hd8:mount=| 1BC220170 74727565 3A616363 6F756E74 3D66616C |true:account=fal| 1BC220180 73653A71 756F7461 3D6E6F00 00000000 |se:quota=no.....| 1BC220190 00000000 00000000 00000000 00000000 |................| 1BC2201A0 00000000 00000000 00000000 00000000 |................| 1BC2201B0 00000000 00000000 00000000 00000000 |................| 1BC2201C0 00000000 00000000 00000000 00000000 |................| 1BC2201D0 00000000 00000000 00003433 64393432 |..........43d942| 1BC2201E0 62342E31 31000000 00000000 00000000 |b4.11...........| 1BC2201F0 00000000 00000000 00000000 00000000 |................|

FS descriptor eleje:

1BC228000 4A324653 00000002 00000000 527FF62C |J2FS........R..,| 1BC228010 00000000 00000000 00000000 00002F76 |............../v| 1BC228020 61722F61 00000000 00000000 00000000 |ar/a............|

Na, ki veszi észre mi a hiba az utóbbi példában? :) Igen, az FS descriptorban lévő adat nem egyezik meg az LVM descriptorban lévő labellel. Hogy miért? Azért mert az FS descriptorban lévő adat csak 6 karakter hosszú minden eseteben.
Jó, következő kérdés: Ennek így mi értelme? Nos, az AIX a boot folyamán ezt a 6 karaktert használja a boot során (Step 2, nem összekeverni az rc2-vel!) hogy visszaellenőrizze az FS-t adatait (felmerült már bárkiben, hogy a rendszer indításához szükséges fájlrendszerek mount pointja miért olyan rövid mindig? :))
Na jó.. Innentől a kérdés már csak az, hogy a mount pointok akkor hol vannak letárolva? Hát ez az.. A /etc/filesystems file alatt. És csak ott. Minden más csak "tájékoztató jellegű", kb. mint a MÁV menetrendje.

Visszatérve az eredeti szálra már látszik, hogy milyen csapdáról beszéltem: Az importálás során ezek a definíciók nem elérhetőek a /etc/filesystems alatt, az FS descriptorra se lehet támaszkodni ilyenkor, így hát marad az LVM descriptorban lévő Label.
Igen ám, de mi van akkor ha valaki megváltoztatja egy FS mount pointját és ehhez nem a megfelelő chfs parancsot használja, hanem manuálisan szerkeszti a /etc/filesystems filet? Pontosan.. Inkonzisztencia.. Minden szépen működni is fog egészen addig, amíg az ember egy ehhez hasonló VG importot nem akar végrehajtani (hisz a rendszer minden boot során ezen file alapján fogja bootolni a filerendszereket), amikor is az importvg/recreatevg neme egyszerűséggel felülvágja a /etc/filesystems-ben található adatokat a Label-ben található adatokkal.

Jó.. már értem.. Azt hiszem.. Hogy tudok meggyőződni arról, hogy nálam ilyen nem fordul elő?
A legegyszerűbben az lslv kimenetéből tudod megnézni a Label-t. Ha abban a szerencsétlen helyzetben találod magad, hogy nem tudod lslv-vel lekérdezni az LV-ket, de a diszkek még elérhetőek (és nem akarsz egy importvg-t megkockáztatni), akkor ezzel a kis scripttel le tudod kérni direktben a diszkekről a label-eket:

Példa kimenet:

# read_labels_directly_from_disk.sh hdisk0 hdisk0 / smallvg / 64 MB PP size ******************* ....primary_bootlv.............................. / hd5 - 64 MB / 1 (220000) / hdisk0 ....None........................................ / hd6 - 512 MB / 104 (19C220000) / hdisk0 ....None........................................ / hd8 - 64 MB / 206 (334220000) / hdisk0 ..../........................................... / hd4 - 192 MB / 207 (338220000) / hdisk0 .#../usr........................................ / hd2 - 2240 MB / 210 (344220000) / hdisk0 ..../var........................................ / hd9var - 256 MB / 241 (3C0220000) / hdisk0 ..../tmp........................................ / hd3 - 768 MB / 245 (3D0220000) / hdisk0 ..../home....................................... / hd1 - 64 MB / 247 (3D8220000) / hdisk0 ..../opt........................................ / hd10opt - 384 MB / 248 (3DC220000) / hdisk0 ..../admin...................................... / hd11admin - 128 MB / 250 (3E4220000) / hdisk0 ..../var/adm/ras/livedump....................... / livedump - 256 MB / 112 (1BC220000) / hdisk0 ....None........................................ / dumplv - 1280 MB / 479 (778220000) / hdisk0 ....None........................................ / dumplv2 - 1280 MB / 269 (430220000) / hdisk0

# Érdekesség: A módszer kicsit más és más VG típusonként, így aki érdeklődik a tényleges struktúra után, az nyugodtan kukkantson bele a scriptbe :) (kis matek tudás azért nem fog ártani)

2012. február 14., kedd

fuser - némileg másként

A múltkor blogbejegyzés után kedvet kaptam némi kis turkászásban a kernel térben, úgy hogy íme egy újabb szösszenet arról, hogy a rendszernek mit is kell csinálnia amikor mi csak úgy dobálózunk a parancsokkal :)

Na szóval.. A mostani alkalomra a fuser nevű parancsot néztem ki magamnak.. Sokat nem kell róla mondani: Általános ismérve, hogy ha egy FS nyitva van, akkor ezt szoktuk meghívni, hogy megnézzük mi is fogja. Nézzük, hogy ezt a funkciót a kernel térből hogy is lehetne elérni:

Első körben kell nekünk egy mount point.. Az én esetemben ez a /opt/IBM lesz, mert miért ne :)

Első körben a kontroll teszt: Nézzük, hogy a rendszer mit is tud róla:
test_server@root:/ # fuser -cux /opt/IBM
/opt/IBM:  1130574c(root) 1253494c(root)
Szupi.. Na akkor nézzük ugyan ezt a kernel térben. A módszer az alábbi dióhéjban:
- Meg kell keresni az adott mount point memória pointerjét
- Ezt a pointert használva megnézni az elérhető I-node bejegyzések pointereit, majd leválogatni amire van bármiféle hivatkozás
- A leválogatott hivatkozásokat alapul véve meg kell nézni, hogy melyiknek mi a Gnode (generic node structure) szegmense
- Ezen segmensekből már le tudjuk válogatni, hogy melyiknek milyen thread a szülője, amiből meg direkt megtalálhatjuk a thread-hez rendelt PID-et.

A valóságban:
Első körben ugye a mount-hoz tartozó cím kell. Annyi probléma van, hogy a mount-ot kikérve ilyen összevisszaságot kapunk:
(0)> mount
                           GFS             DATA TYPE    FLAGS
  1 F10001001D6868B0 014D4D60 F1000100206D2080 JFS2    DEVMOUNT
... /dev/hd4 mounted over /
  2 F10001001D686950 014D4D60 F1000100206A4480 JFS2    DEVMOUNT
... /dev/hd2 mounted over /usr
  3 F10001001D6869F0 014D4D60 F1000100206FB080 JFS2    DEVMOUNT
... /dev/hd9var mounted over /var
  4 F10001001D686810 014D4D60 F1000100206FC880 JFS2    DEVMOUNT
... /dev/hd3 mounted over /tmp
És így tovább.. Ez persze felvet 2 gondot:
- Komolyan végig kell mindent böngésszek, úgy hogy közben kifolyik a szemem?
- Miért nem lehet ezt 1 sorban kirakni, hogy legalább greppelni lehessen rá?

Persze azért annyira mégse rossz a helyzet, csak kicsit bűvészkedni kell az awk-al, és nyomban meglesz az ami nekünk kell:
(0)> mount |awk '{a[NR]=$0} /\/opt\/IBM$/ {print a[NR],a[NR-1]}'
... /dev/IBMlv mounted over /opt/IBM  14 F100010020684550 014D4D60 F1000100226C2C80 JFS2    DEVMOUNT

Ugye hogy így szebb.. Na szóval megvan a pointerünk (F100010020684550). Akkor kérjük le milyen i-node bejegyzések vannak alatta:
(0)> mount F100010020684550 |grep -E "DIR|REG|SOCK" |tail -5
 648 F1000100241643E8     0 F1000100241640B0 F100010020684550 DIR
 649 F1000100240E03E8     0 F1000100240E00B0 F100010020684550 DIR
 650 F100010024092FE8     1 F100010024092CB0 F100010020684550 REG
 651 F10001002411B7E8     2 F10001002411B4B0 F100010020684550 DIR
 652 F1000100226D2FE8     1 F1000100226D2CB0 F100010020684550 DIR  ROOT
Na itt egy újabb probléma.. Mi ez a sok szám, és mit jelentenek? Nos A válasz a következő:
- Az első és második oszlop lényegében ugyan azt takarja. Az első az index-szám, a második pedig a VFS_VNODE pointer.
- A 3. szám egy úgy nevezett counter. Ez mutatja, hogy hány kernel thread használja éppen az adott VFS_VNODE-ot.
- A 4. lesz a VFS_VNODE-hoz tartozó GNODE.
- Az 5.-et most bocs de kihagyom, mert érdektelen jelen szempontból

Ami nekünk ezek közül kell, az azok a GNODE regiszterek, amelyeknek COUNT-ja nagyobb mint 0. Ezt kb az alábbi scriptelési módszerrel tudjuk előhozni:
(0)> mount F100010020684550|grep -E "DIR|REG|SOCK" |awk ' { if ($3 != "0") print }'
 154 F100010036F6A3E8     1 F100010036F6A0B0 F100010020684550 REG
 157 F100010036F3A3E8     1 F100010036F3A0B0 F100010020684550 REG
 160 F100010036F99FE8     1 F100010036F99CB0 F100010020684550 REG
 163 F100010036F69FE8     1 F100010036F69CB0 F100010020684550 REG
 266 F100010036FA6FE8     1 F100010036FA6CB0 F100010020684550 REG
 556 F1000100244D2FE8     1 F1000100244D2CB0 F100010020684550 REG
 557 F1000100243B57E8     1 F1000100243B54B0 F100010020684550 REG
 558 F1000100244CC3E8     1 F1000100244CC0B0 F100010020684550 REG
 559 F1000100244EBBE8     1 F1000100244EB8B0 F100010020684550 REG
 560 F1000100243B0BE8     1 F1000100243B08B0 F100010020684550 REG
 561 F1000100244CB7E8     1 F1000100244CB4B0 F100010020684550 REG
 562 F1000100244A93E8     1 F1000100244A90B0 F100010020684550 REG
 563 F1000100244C8BE8     1 F1000100244C88B0 F100010020684550 REG
 652 F1000100226D2FE8     1 F1000100226D2CB0 F100010020684550 DIR  ROOT

Egy baj van - Ez a lista helyenként iszonyat nagy.. Itt se épp rövid.. A problémám meg az, hogy az itt található GNODE ID-kat kéne mind végigjárjam az alábbi módon:
(0)> gnode F1000100243B08B0 |grep gn_seg
gn_seg........ 00000000000086A6
(0)> scb 2 00000000000086A6 |grep pvproc
pvproc pointer           (pvproc)   : 0000000000000000

(0)> gnode F1000100244C88B0 |grep gn_seg
gn_seg........ 00000000000246AD
(0)> scb 2 00000000000246AD |grep pvproc
pvproc pointer           (pvproc)   : 0000000000000000

(0)> gnode F100010036F99CB0 |grep gn_seg
gn_seg........ 00000000000732D8
(0)> scb 2 00000000000732D8 |grep pvproc
pvproc pointer           (pvproc)   : F100070F00045000
3.-ra csak ráhibáztam az egyikre, de persze ezt így egyesével végigjátszani valami halál.. Mindenesetre ha megvan a pvproc cím, akkor abból már az egyik process-t meg is találhatjuk:
(0)> proc F100070F00045000 |grep ^pvproc
pvproc+045000  276 java     ACTIVE 011404E 0132076 0000000030588400   0 0013
(0)> hcal 011404E
Value hexa: 0011404E          Value decimal: 1130574
Bingo.. 1130574 az egyik PID amint fentebb is láttunk.. Na de akkor hogy is lehet ezt normálisan végignézni, úgy hogy az összes GNODE ID-t végigjátsszuk? Hát, a szomorú hír, hogy a kdb nem tud belső scriptelést, így a shell-re kell támaszkodjunk, ami meg egy részről iszonyat erőforrás zabáló, más részről meg a sok fork miatt ágyúval verébre módszer is. Annyi segítségünk mondjuk van, hogy a kdbnek van '-script' kapcsolója, ami pont az ilyen munkákat igyekszik megkönnyíteni, de azért ez még mindig kevés az optimális működéshez.
test_server@root:/ # for GN_SEG in $(
>       for GNODE_ID in $(echo mount F100010020684550|kdb -script |grep -E "DIR|REG|SOCK" |awk ' { if ($3 != "0") print }')
>       do
>               echo gnode $GNODE_ID|kdb -script|awk '/gn_seg/ {print $NF}'
>       done)
> do
>       echo scb 2 $GN_SEG |kdb -script|awk '/pvproc/ {print}'
> done|sort -u|grep -v 0000000000000000
pvproc pointer           (pvproc)   : F100070F00045000
pvproc pointer           (pvproc)   : F100070F0004C800
Ebből kiindulva már nem nehéz megtalálni azt amire végső soron pályázunk (viszalépés kdb-be, majd.. )
(0)> proc F100070F00045000 |grep ^pvproc
pvproc+045000  276 java     ACTIVE 011404E 0132076 0000000030588400   0 000E
(0)> hcal 011404E
Value hexa: 0011404E          Value decimal: 1130574

(0)> proc F100070F0004C800 |grep ^pvproc
pvproc+04C800  306 sh       ACTIVE 0132076 0000001 0000000008466400   0 0001
(0)> hcal 0132076
Value hexa: 00132076          Value decimal: 1253494

1130574, 1253494. Amiket fentebb is láttunk.. Sőt, még azt is tudjuk, hogy egy sh, meg egy java processről van szó :)
Ha ne adj isten lusták vagyunk visszamenni a fenti script után a kdb-be, akkor némi kis módosítással nyomban ki is lehet kérni az ID-ket:

test_server@root:/ # MOUNT=$(echo mount |kdb -script|awk '{a[NR]=$0} /\/opt\/IBM$/ {print a[NR-1]}'|awk '{print $2}')
test_server@root:/ # for PVPROC in $(
>       for GN_SEG in $(
>               for GNODE_ID in $(echo "mount $MOUNT" |kdb -script|grep -E "DIR|REG|SOCK" |awk ' { if ($3 != "0") print $4}')
>               do
>                       echo gnode $GNODE_ID|kdb -script|awk '/gn_seg/ {print $NF}'
>               done)
>       do
>               echo scb 2 $GN_SEG |kdb -script|awk '/pvproc/ {print $NF}'
>       done|sort -u|grep -v 0000000000000000)
> do
>       echo proc $PVPROC|kdb -script|grep ^pvproc
> done
pvproc+045000  276 java     ACTIVE 011404E 0132076 0000000030588400   0 0017
pvproc+04C800  306 sh       ACTIVE 0132076 0000001 0000000008466400   0 0001

# Mint fent említettem: A módszer csak azokra a GNODE-okra működik aminek a countere (aktív thread-je) nagyobb mint 0. Ez azért problémás, mert ha egy process használ egy file-t, de az épp nem aktív (írás nélküli file, pl tail esetén), akkor ez a módszer is hibádzik. További probléma, hogy a submount-okat (amik szintén fogják az FS-t umount esetén) szintén nem képes kijelezni, szóval nyugodtan tekintsétek ezt is amolyan érdekességnek, tekintve, hogy mind a fuser, mind az lsof bőven alkalmasabb ezeknek a megkeresésére (általában - volt amikor ez a módszer talált meg nekem egy olyan thread-et, amiről más program sose hallott)

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 :)