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

に対応する。