Archive

Archive for the ‘General’ Category

Just small script for toying around with python….

May 12th, 2009

There is always a first time… now this is it.. in English coi…. :D
It happened during administrating servers in detik. Every time doing login to the servers, it always ask the password. There’s mechanism by copying public key into remote server in order to login automatically without entering the password. Application like ssh-copy-id do the job.
So, my concern is how to do login automatically without having explicitly launch ssh-copy-id before ssh to the server. I’ve written a script to handle that. This script requires pexpect library. Here it is :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""
USAGE:

go user@host

This script is used as One Time Password SSH login
"""


import os, pexpect, getpass, sys, socket
def gethomedir():
    for user in open('/etc/passwd'):
        cols = user.split(':')
        if cols[0] == os.getlogin():
            return cols[5]

def knownhost(user_host):
    for line in open(homedir + '/.ssh/hosts', 'a+'):
        if line.strip() == user_host:
            return True
    return False
           
def updateknownhost(user_host):
    fd = open(homedir + '/.ssh/hosts','a+')
    fd.write(user_host + '\n')
    fd.close()
   
if __name__ == "__main__":
    homedir = gethomedir()
    if not len(sys.argv) > 1 or sys.argv[1].find('@') < 0:
                print __doc__
                sys.exit(1)
   
    user,host = sys.argv[1].split('@')
    try:
        host = socket.gethostbyname(host)
    except socket.gaierror:
        print 'Error resolving host'
        sys.exit(1)
    if not knownhost(user + '@' + host):
        # ssh-copy-id and update info
        print 'Hostname %s | User %s' % (host, user)
        result = 0
        exp = pexpect.spawn('ssh-copy-id ' + sys.argv[1])
        while result != 2:
            result = exp.expect(['password', 'publickey,password', 'expecting', pexpect.EOF])
            if result == 0:
                # try again
                passwd = getpass.getpass('Enter Password:')
                exp.sendline(passwd)
            elif result == 1 or result == 3:
                # failed to input password correctly
                print 'Failed.'
                sys.exit(1)
        # update info
        updateknownhost(user + '@' + host)
        exp.close()
    os.system('ssh ' + ' '.join(sys.argv[1:]))

And also I’ve created small script to manipulate /etc/hosts, because sometime I don’t remember IP address of some servers, so I just need to create an alias in /etc/hosts. Here is the script :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""
USAGE:

hosts add address hostname... | del [address|hostname]...

This script adds/deletes hostname alias in /etc/hosts
"""


import os, getpass, sys, socket

lineread={}
linewrite={}

def addhosts(address, hostname):
        global linewrite, lineread
        notFound = True
        for idx, line in linewrite.iteritems():
                cols = filter(lambda x:not x == '', line.split())
                if cols[0] == address:
                        notFound = False
                        for host in hostname:
                                if not host in cols:
                                        cols.append(host)
                        linewrite[idx] = ' '.join(cols) + "\n"
        if notFound:
                lineread[len(lineread)] = address + ' ' + ' '.join(hostname) + "\n"

def delhosts(addr_host):
        global linewrite, lineread
        lw = linewrite.copy()
        isAddr = False
        for idx, line in lw.iteritems():
                cols = filter(lambda x:not x == '', line.split())
                for item in addr_host:
                        if item in cols:
                                if cols[0] == item:
                                        isAddr = True
                                        del linewrite[idx]
                                        del lineread[idx]
                                else:
                                        i = 1
                                        for col in cols[1:]:
                                                if col == item:
                                                        del cols[i]
                                                i+=1
                                        if len(cols) == 1:
                                                isAddr = True
                                                del linewrite[idx]
                                                del lineread[idx]
                if not isAddr:
                        linewrite[idx] = ' '.join(cols) + "\n"

if __name__ == "__main__":
        if not len(sys.argv) > 2 or not sys.argv[1] in ['add','del']:
                print __doc__
                sys.exit(1)

        idx=0
        try:
                for line in open('/etc/hosts'):
                        lineread[idx]=line
                        if not (line.strip().startswith('#')) and not (line.strip() == ''):
                                linewrite[idx]=line                    
                        idx+=1
        except IOError:
                print 'Error opening /etc/hosts'
                sys.exit(1)
       
        if sys.argv[1] == 'add':
                addhosts(sys.argv[2],sys.argv[3:])
        else:
                delhosts(sys.argv[2:])
       
        hosts = open('/etc/hosts','w')
        for idx, line in lineread.iteritems():
                if linewrite.has_key(idx):
                        hosts.write(linewrite[idx])
                else:
                        hosts.write(lineread[idx])

admin General

Menganalisa log mysqld.log

March 17th, 2009

Sekalian udah lama gak ngeblog.. sekarang ngisi lagi, meskipun gak penting… hahahaha…

Oke, terkait dengan aktifitas analisa log web di openx (ini seharusnya posting tersendiri lagi) untuk mengetahui response times dan request per seconds. Dari hasil log diketahui bahwa request yang paling lama responsenya (lebih dari 1 detik) adalah script-script yang menjalankan aktifitas database. Meskipun di lihat dari dokumentasi openx, sebenarnya request ke database di cache menjadi file, yang kemudian request-request selanjutnya yang sama akan mengambil di file. Tapi bagaimanapun juga, penulis masih penasaran, apa bener dia ngecache ke file, gak melakukan request yang sama berulang-ulang ke database.

Untuk itu penulis coba buat script pake python untuk analisa mysqld.log. Ini log yang mencatat aktifitas DML dan juga DDL. Penulis hanya tertarik pada request query SELECT saja, karena request inilah yang bisa dicache pake memcached nantinya. Tapi untuk mengetahui pasti, apa bener ada request SELECT yang sama berulang-ulang, yaaa…. pake script python ini.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hashlib, sys
def readlines(fl,sep='\n'):
    line=''
    initial=0
    data=fl.read(1024)
    while data!='':
        possep=data.find(sep,initial)
        if possep==-1:
            line=line+data[initial:]
            possep=initial=0
            data=fl.read(1024)
        else:
            line=line+data[initial:possep]
            yield line
            line=''
            initial=possep+len(sep)
    if line!='':
        yield line

for line in readlines(open(sys.argv[1]),sep='\t\t'):
    pos = line.find('SELECT',0)
    if pos != -1:
        print hashlib.md5(line[pos:]).hexdigest()

Intinya parse per line query SELECT-nya, kemudian di md5sum. Dengan begitu keliatan jika md5sumnya sama, berarti querynya sama persis. Coba di test di satu hari saja.
Panggil parsemysql.py|wc -l, hasilnya 973954 baris. Setelah itu panggil parsemysql.py|uniq|wc -l, hasilnya 969174 baris.
Wadow… kok selisihnya gak jauh ya…. padahal berharap selisih banyak, biar bisa dioptimasi pake memcached. Ya udah…. pupus deh :( …. Berarti emang bener, request udah dicache di file oleh openx.

—- Updated —–
Blo’on…. keliru ternyata cara cari digest yang sama. Seharusnya di sort dulu, baru di uniq…. Semprul…
Percobaan kedua di lakukan; sama dengan sebelumnya, yang pertama panggil parsemysql.py|wc -l, hasilnya 1013913 baris. Setelah itu baru menghilangkan yang sama dengan cara parsemysql.py|sort|uniq|wc -l, hasilnya 20739 baris. Jauh banget euiii… kalo gini caranya, ya pake memcached dong….

admin General

Poison message di Message Driven Bean

May 7th, 2008

Sebenarnya solusinya agak sedikit kurang bagus sih untuk mengatasi masalah poison message di MDB ini, tapi bagaimana lagi….. Poison message itu sebenarnya message yang diterima oleh MDB dari JMS sedemikian rupa sehingga waktu diolah di MDB-nya membuat error di bisnis logic. Sehingga exception dilempar dan diketahui oleh container EJB, kemudian melakukan proses rollback transaction. Rollback transaction akan diikuti proses redelivery message oleh JMS ke recipent, dalam hal ini MDB itu sendiri. Jika itu berlanjut, maka otomatis proses akan berulang-ulang tanpa henti. Ini bisa menghabiskan load di server.

Nah, berhubung penulis masih belajar :D , maka salah satu solusinya (sesuai dengan yang dibuku sih) adalah dengan memberi penanda khusus untuk tiap message ID. Jadi jika proses sudah berulang kali dilakukan dengan message ID yang sama, maka jika sudah melebihi batas redelivery message count-nya, message akan di discard dan tidak masuk dalam bisnis logic. Dengan begitu MDB akan berjalan aman, gak akan terjadi pengulangan proses dengan message ID yang sama.

Tapi sebenarnya yang mengganjal itu adalah proses catch exception yang dikeluarkan oleh JDBC (pake JPA). Karena memang tidak tertangkap baik di sisi MDB-nya. Jadinya akal-akalan dengan melihat contoh kasus di forum, yang menggunakan entity manager untuk menyimpan ke DB; dengan cara setelah memanggil command persist, harus diikuti oleh command flush. Dengan begitu exception yang dikeluarkan di bisnis logic (khususnya di JPA-nya) dapat di catch. Meskipun penulis pikir yang ke catch itu EJBException-nya, soalnya sebelumnya dilihat di log, proses berulang-ulang itu akan throw 2 exception, satu adalah toplink JDBCnya dan EJBException sendiri (ini kayaknya bagian containernya).

Saat ini udah kehandle pake cara tadi itu, cuman ya itu tadi; belum ketemu untuk nge-catch Database Exceptionnya…. yah … sambil jalan aja lah…..

admin General

Fix one, Open another

April 22nd, 2008

Baru nyadar kenapa validasi user dan password waktu sudah login gak bisa dimasukkan ke variable session PHP. Ternyata feature ‘remember me’ jadi gak berguna. Sebab session gak bisa hidup terus, jadinya percuma simpan cookie user dan pass untuk expired 1 tahun.

Solusinya ya, terpaksa dibedain/dikeluarin dari variable session. Bikin data store sendiri (masih pake memcached) dengan menyimpan user dan password. Jadi kalo diset expired lama, memcached ini masih bisa. Konsekuensinya juga di memcached tidak selamanya data disimpan. Hm.. jadi tricky. Semakin lama, semakin jauh dari kondisi ideal. Dan kerjaan masih ada lagi. Terpaksa deh pending.. weh… sedih kalo muncul masalah lain.

…. lagi bad mood hari ini …..

admin General

Progres di blogdetik.com…

April 21st, 2008

Setelah mendeploy arsitektur baru blogdetik di mesin server yang baru, masih ada kendala yang dihadapi. Yang awal tampak adalah penggunaan session di WPMU. Session disimpan di server memcached dengan model replication, dimana jika salah satu server mati, akan switch fail over ke server satunya. Sebenarnya alokasi server memcached ini aslinya akan dipakai untuk aplikasi lainnya yang membutuhkan cache data, tapi untuk sementara ini dipakai buat blogdetik. Perkiraan awal meleset karena fail over menggunakan ucarp masih belum pas. Jika server master proses mati, tidak akan mentrigger fail over ke server slave. Untuk itu, harus dikendalikan sendiri dari sisi aplikasi. Untuk itu, harus ada tambahan script untuk menghandle session store diluar default session store milik memcache.

Disela-sela development, ditemukan masalah lain yang berhubungan dengan session. Salah satunya adalah plugin LDAP untuk authentication. Ada sedikit vulnerability dari validasi user. Cookie yang dibuat untuk validasi (wordpressuser dan wordpresspass) masih rentan terhadap serangan. Setelah memantau di kodenya, rupanya cookie pass diset menggunakan md5 username dan md5 ldap cookie marker, yang jelas kelihatan rentan untuk ditembus. Seharusnya representasi password benar-benar dari entitas password itu sendiri. Seperti di standar WPMU, validasi menggunakan md5 password yang dipassing ke cookie. Jadi setiap pemanggilan URL ke aplikasi, validasi dilakukan dengan pengecekan md5 password yang tersimpan diserver. Standar WPMU menyimpannya di file cache. Ini juga menimbulkan pertanyaan, kenapa tidak bermain-main dengan variable session. Disini penulis mulai mengupas pemakaian session di WPMU.

Ditemukan lagi pertanyaan, kenapa untuk masing-masing subdomain dari blogdetik harus membuat session lagi ? Kenapa gak dijadikan satu saja ? Disini lagi-lagi penulis coba untuk melihat penyebabnya. Dan… gak ketemu pastinya, tapi secara default WPMU rupanya tidak terlalu fokus untuk masalah ini. Okelah, sepertinya sekalian aja dirapikan untuk masalah session ini. Penulis berusaha menggunakan fasilitas variabel session yang ada di PHP. Untuk lebih meningkatkan sekuriti, akhirnya dipakailah variabel session untuk menyimpan md5 password (ini untuk meminimalisir load pengecekan ke database LDAP setiap pemanggilan URL).

Oh, ya; sebelumnya juga ditemukan masalah di plugin firestats. Plugin ini rupanya membuat mekanisme handling session sendiri diluar standar. Firestats memakai file based session, yang untuk sementara direktori dialihkan ke NFS server untuk sentralisasi. Akhirnya untuk sekalian menyeregamkan pemakaian session, penulis juga harus merubah management session ke satu bentuk management session di memcached.

Hasilnya, session management dibuat fasilitas fail-over. Dengan tambahan jika memang kedua server memcached sedang bermasalah, akan dialihkan ke cadangan file based session yang disimpan di direktori NFS mounted. Metode persistent connection gak berjalan mulus di server memcached. Ini ada hubungannya dengan php-cgi dalam menghandle exception untuk koneksi memcached yang bermasalah. Setelah mengganti ke bentuk non persistent, error gateway time out dikarenakan php-cgi yang bermasalah tidak terjadi lagi. Tapi konsekuensinya adalah banyaknya status koneksi time-wait. Untuk menghindarinya, set 1 ke parameter kernel tcp_tw_recycle & tcp_tw_reuse.

Selain itu semua subdomain di blogdetik menggunakan satu domain (.blogdetik.com). Dengan begitu sesi cookie akan kelihatan lebih bersih aja dibandingkan sebelumnya. Firestats plugin mendukung variabel memcached-based session.

Beberapa perbaikan kecil juga dilakukan di konfigurasi nginx.conf. Yakni masalah pemanggilan direktori (seperti wp-admin) tanpa “/”, yang mengakibatkan kesalahan pembuatan link di administrator panel. Solusinya harus redirect URL dengan penambahan “/” diakhir, jika URL memang adalah direktori. Masalah lain adalah set default comment_registration saat create user baru, untuk mencegah spam comment untuk non-user blogdetik. Feature supercache dan wp-cache juga di ulas kembali untuk kasus kontak. Penyelesainnya dengan cara memasukkan URL tersebut di daftar excluded URL di administrasi supercache dan wp-cache.

Berikut file-file yang mengalami perubahan dengan detil masalahnya:

  1. remove redundant session (wp-config.php) => change index.php, captca.php, kontak.php, wp-cache-config.php (add kontak.php as no cache)
  2. fix nginxmu.conf for redirecting directory access.
  3. fix password cookie (wp-settings.php (avoid clears GLOBAL var), ldap_auth.php(authorization/update pass;set session), wp-login.php (logout;clear session and sessionid)).
  4. create session memcached failover file based (failover seem trouble, cause persistent connection from memcache) => use manual if both servers down => use non persistent, then no need manual failover for file.
  5. session firestats used default php session => plugin/firestats/php/session.php
  6. set comment_registration 1 as default (wp-admin/includes/schema.(original|innodb).php)

Tapi masih ada todo list untuk masalah lainnya yang masih belum selesai, yakni:

  1. php-cgi fault.
  2. captca vulnerability.
  3. unknown directory created.
  4. yahoo mail problem.
  5. software monitoring.
  6. mobile themes.

admin General, Server