substituição de cromo tolerante a falhas

3

Estou procurando um substituto para o crond, ou possivelmente uma extensão dele.

O recurso que eu definitivamente gostaria de ter é a tolerância a falhas. Por exemplo. se um trabalho não foi executado devido ao computador não estar ligado no tempo especificado (por exemplo, devido a uma falha de energia) ou se a tarefa não foi executada com êxito (ou seja, rc! = 0) (por exemplo, sem acesso à Internet) então o software em questão deve tentar periodicamente até a próxima execução programada, ponto em que continuaria sua operação regular; assumindo que a corrida foi um sucesso.

Outras funcionalidades agradáveis:

  • controle remoto via, e. uma interface REST
  • melhor registro

Caso não exista tal software disponível, alguém poderia me indicar na direção certa qual é a melhor ideia: uma extensão de um software existente ou escrever algo do zero?

    
por ParaDoX 27.10.2014 / 10:41

1 resposta

2

Eu tenho vários trabalhos que exigem execução pelo menos uma vez por dia. O que eu faço é iniciar os scripts para esses trabalhos a cada hora (ou com mais frequência) e os scripts deles verificam se já foram executados, verificando um arquivo de status no disco.

Se o arquivo de status existir e estiver atualizado, o script será encerrado.

Se esse arquivo for antigo (ou seja, gravado pela última vez no dia anterior) ou não existir, o script será executado e, no término bem-sucedido, o arquivo de status será gravado.

Se você não puder criar essa funcionalidade em um programa existente, é simples criar um script de wrapper que verifique se o programa deve ser executado, chama o programa, se necessário, e em caso de sucesso (valor de saída, saída analisada) grava o arquivo de status.

/usr/local/bin/catchup.simple :

#! /usr/bin/env python

"""
first parameter is a path to a file /..../daily/some_name
That is a status/script file and the /daily/ indicates it needs to run at least
once a day (after reboot, after midnight).

The rest of the parameters is the command executed and its parameters.
If there are no more parameters beyond the first the actual status
file is /..../daily/some_name.status and is expected to be updated by calling
the /....daily/some_name script (which has to be executable). That
script doesn't need to know about the frequency and gets called with
the status file as first (and only) argument.

Valid directory names and their functioning:

   /daily/  run once a day (UTC)
   /hourly/ run once an hour

The actual scheduling and frequency to check if running is necessary, is
done using a crontab entry:

CU=/usr/local/bin/catchup.simple
CUD=/root/catchup

# month, hour, day_of_month, month day_of_week command
*/5 * * * * $CU $CUD/daily/getlogs curl ....

If mulitple days (or hours) have gone by, no runs are made for skipped
days.

If subprocess.check_output() fails the status file is not updated.
"""

import sys
import datetime
import subprocess

verbose = False  # set to True to debug

def main():
    if len(sys.argv) < 2:
        print 'not enough parameters for', sys.argv[0]
        return
    if len(sys.argv) == 2:
        status_file_name = sys.argv[1] + '.status'
        cmd = [sys.argv[1]]
    else:
        status_file_name = sys.argv[1]
        cmd = sys.argv[2:]

    freq = sys.argv[1].rsplit('/', 2)[-2]
    if verbose:
        print 'cmd', cmd
        print 'status', status_file_name
        print 'frequency', freq
    try:
        last_status = datetime.datetime.strptime(
            open(status_file_name).read().split('.')[0],
            "%Y-%m-%dT%H:%M:%S",
        )
    except (IOError, ValueError):
        last_status = datetime.datetime(2000, 1, 1)

    now = datetime.datetime.utcnow().replace(microsecond=0)
    if verbose:
        print last_status
        print 'now', now.isoformat()
    if freq == 'daily':
        if last_status.date() < now.date():
            subprocess.check_output(cmd)
        elif verbose:
            print 'already done today'
    elif freq == 'hourly':
        if last_status.date() < now.date() or \
           last_status.date() == now.date() and \
           last_status.hour < now.hour:
           subprocess.check_output(cmd)
        elif verbose:
            print 'already done this hour'

    with open(status_file_name, 'w') as fp:
        fp.write(now.isoformat())

if __name__ == "__main__":
    main()
    
por 27.10.2014 / 10:51

Tags