Func: Fedora Unified Network Controller を使ってみた(6.2)
前回 ( http://d.hatena.ne.jp/aaabbb_200904/20090922/1253627352 ) のような方針で。Pythonでスケジューラを作るとすると、 /etc/cron.d のようにPythonモジュールを置いておくディレクトリを作り、そのディレクトリ以下のファイル名を全て取得し、それらをPythonモジュールとしてimportする必要がある。
通常の import 文だと、引数としてstringを指定できないのだが、
sys=__import__('sys')
のような感じで、__import__を実行すると、上手くstringをimportできるらしい。
また、あるディレクトリ以下のファイル名を取得するには、
import os os.listdir()
が使用できる。
実際に、こんな感じでjob1, job2などのスケジュールを持ったディレクトリがある時、
$ ls -R cron.d/ cron.d/: holidays job1 job2 cron.d/holidays: __init__.py __init__.pyc september.py september.pyc cron.d/job1: __init__.py __init__.pyc row2.py row2.pyc cron.d/job2: __init__.py __init__.pyc row2.py row2.pyc
各スケジュールをモジュールとして読み込むには、次のようにすればよいようだ。
crond_path='cron.d' sys.path.append(crond_path) crontabs = os.listdir(crond_path) crontabs.remove('holidays') users=[] for c in crontabs: try: users.append(__import__(c)) except Exception, inst: print repr(inst)
なお、途中、crontabsから、holidaysを除いているが、こちらは休日データを収納するためで、直接スケジュールとして使っていないことによる。
メインのスケジュール部分はこんな感じになった。
while True: t=time.localtime() month=t[1] day=t[2] hour=t[3] min=t[4] second=t[5] dow=t[6] if (not second==0): time.sleep(0.1) continue print t for u in users: print repr(u) l=u.l for m in l: print repr(m) if (m.check_min(min) and m.check_hour(hour) and m.check_day(day) and m.check_month(month) and m.check_dow(dow)): print 'condition satisfied' if (m.command == None): print "don't perform command" break else: print 'perform command' thread=threading.Thread(target=m.command) try: thread.start() except Exception, inst: print repr(inst) else: print 'condition not satisfied' print "\n" time.sleep(1)
重要な部分は、
while True: if (not second==0): time.sleep(0.1) continue
のように、秒が0でないときには、sleepをはさんで、待ちにしているあたりと、
thread=threading.Thread(target=m.command) try: thread.start() except Exception, inst: print repr(inst)
threading と例外を使って、各モジュールが長時間かかる場合にスケジューラが引きずられないようにすることと、モジュール自体に文法ミス, sys.exit などがあった場合にスケジューラが落ちないようにすることだろうか。。
ちなみに、cron.d/job1 の中身も載せておく。
$ cat cron.d/job1/__init__.py import holidays, row2 l=[holidays.september, row2] $ cat cron.d/job1/row2.py import subprocess, os, time def command(): time.sleep(5) p=subprocess.Popen('ps') pid, sts = os.waitpid(p.pid,0) print pid, sts def check_min(n): return True def check_hour(n): return True def check_day(n): return True def check_month(n): return True def check_dow(n): return True
なお、job1は、crontabでいうと、
* * * * * ps
に対応する。