예제 #1
0
def info(hash):
    if hash == '0':
        try:
            schedules = crontab_manager.get_tasks_with_info()
        except Exception as exc:
            app.logger.error('Ошибка при получении crontab: %s', exc)
            flash(str(exc))
            schedules = []
        return render_template('info.html',
                               full_name=AppConfig.get_server_name(),
                               remote=False,
                               schedules=schedules,
                               sorted_schedules=sorted(schedules),
                               actions=AppConfig.conf().get('actions'),
                               sorted_actions=sorted(
                                   AppConfig.conf().get('actions')),
                               logs=Logging.get_logs_list())
    else:
        s = RemoteServers.find_server(hash)
        if s is None or not s.state:
            return redirect(url_for('servers'))
        resp = RemoteServers.get_remote_server_config(s.url)
        if len(resp) < 2:
            from collections import defaultdict
            flash(resp.get('status', 'Возникла неизвестная ошибка'))
            resp = defaultdict(dict)
        return render_template('info.html',
                               full_name=resp['full_name'],
                               hash=s.hash,
                               remote=True,
                               schedules=resp['schedules'],
                               sorted_schedules=sorted(resp['schedules']),
                               actions=resp['actions'],
                               sorted_actions=sorted(resp['actions']),
                               logs=resp['logs'])
예제 #2
0
    def get_config(self):
        conf = {}
        conf['host'] = '127.0.0.1'
        conf['port'] = 5556
        if AppConfig.conf().get('web'):
            conf['host'] = AppConfig.conf().get('web').get('host', '127.0.0.1')
            conf['port'] = AppConfig.conf().get('web').get('port', 5556)

        return conf
예제 #3
0
class webappconf:
    HOST = '127.0.0.1'
    if 'web' in AppConfig.conf().keys():
        HOST = AppConfig.conf().get('web').get('host', '127.0.0.1')
    PORT = 5555
    if AppConfig.conf().get('web') and 'port' in AppConfig.conf().get(
            'web', {}).keys():
        PORT = AppConfig.conf().get('web').get('port', '5555')
    CSRF_ENABLED = True
    SECRET_KEY = 'you-will-never-guess'
    LOGIN_MESSAGE = u'Пожалуйста, авторизуйтесь для доступа к этой странице.'
    DEBUG = False
예제 #4
0
    def start_task(self):
        """Запускает последовательное выполнение сформированных действий.

        Если в конфигурации параметр allow_parallel имеет значение
        False, то перед запуском сначала происходит проверка отсутствия
        параллельных процессов выполнения заданий и только потом запуск.
        """
        if not AppConfig.conf().get('allow_parallel', True):
            keywords = [get_executable_filename(), constants.RUN_OPT_NAME]
            pid = check_process(keywords)
            if pid:
                self.logger.error(
                    'Другое задание уже запущено: PID: %s',
                    pid,
                )
                sys.exit(-1)

        for action in self.actions:
            self.logger.info('Запускается действие %s', action.name)
            try:
                success = action.start()
            except KeyboardInterrupt:
                self.logger.warning('Выполнение прервано нажатием Ctrl+C')
                break

            if success:
                self.logger.info('Выполнено действие %s', action.name)
            else:
                self.logger.error(
                    'Действие %s выполнено неудачно, выполнение остановлено',
                    action.name,
                )
                break
예제 #5
0
    def create_actions(self):
        """Создаёт действия по описанию из self.action_records.

        Вызывает self._create_action для всех записей из self.action_records
        и делает дополнительную обработку (добавляет флаг --dry).

        """
        all_actions_records = AppConfig.conf().setdefault('actions', {})
        action_builder = ActionBuilder(all_actions_records)

        for action_record in self.action_records:
            dry = False

            if isinstance(action_record, list):
                args = arguments.parse_action_record(action_record)
                action_record = args.action_name
                dry = args.dry

            try:
                action = action_builder(action_record)
            except Exception as exc:
                self.logger.error(
                    'Ошибка при создании действия %s: %s',
                    action_record,
                    exc,
                )
                sys.exit(-1)

            if action is not None:
                if dry:
                    action.dry = dry
                    self.logger.debug('Добавлен флаг dry')
                self.actions.append(action)
                self.logger.debug('Действие добавлено')
예제 #6
0
    def is_active(self, name):
        """Проверяет активно ли текущее задание.

        Args:
            name: Строка, имя задания

        Returns:
            bool, активно или нет

        Raises:
            OSError: нет доступа к crontab необходимого пользователя

        """
        user = AppConfig.conf().get('cron', {}).get('cron_user', CURRENT_USER)
        try:
            cron = CronTab(user)
        except OSError as exc:
            exc = OSError(exc)
            exc.__cause__ = None
            raise exc

        for job in cron:
            if job.comment == name:
                return True
        return False
예제 #7
0
def get_trigger_filepath():
    """Возвращает путь к триггеру.

    Рекурсивно создаёт директорию необходимую, если на момент
    инициализации её не существовало.
    Если в конфигурации отстутствует параметр logging.trigger_filepath,
    то возвращает None.

    Returns:
        Строка, путь к триггеру, или None.

    """
    try:
        conf = AppConfig.conf()
    except (FileNotFoundError, ConfigError):
        trigger_filepath = None
    else:
        trigger_filepath = conf.get('logging', {}).get('trigger_filepath')

    if not trigger_filepath:
        return trigger_filepath
    trigger_dirpath = os.path.dirname(trigger_filepath)
    if trigger_dirpath and not os.path.exists(trigger_dirpath):
        os.makedirs(trigger_dirpath)
    return trigger_filepath
예제 #8
0
def get_log_dirpath(subdir=None):
    """Возвращает путь к директории логирования.

    Если директория не сущестувет, то она создаётся.

    Args:
        subdir: Строка или None, требуемая поддиректория.

    Returns:
        Строку, путь к существующей директории.

    """
    try:
        conf = AppConfig.conf()
    except (FileNotFoundError, ConfigError):
        path = os.path.abspath(DEFAULT_LOGS_PATH)
    else:
        path = conf.get('logging', {}).get(
            'logs_path',
            os.path.abspath(DEFAULT_LOGS_PATH),
        )
    if subdir:
        path = os.path.join(path, subdir)
    if not os.path.exists(path):
        os.makedirs(path)
    return path
예제 #9
0
def get_server_config():
    result = {
        'full_name': AppConfig.get_server_name(),
        'schedules': crontab_manager.get_tasks_with_info(),
        'actions': AppConfig.conf().get('actions'),
        'logs': get_logs_list()
    }
    return result
예제 #10
0
def addSchedule():
    form = ScheduleForm()
    if form.validate_on_submit():
        try:
            crontab_manager.update_task(form.name.data, form.cron.data,
                                        form.descr.data, form.actions.data)
        except Exception as exc:
            flash('Произошла ошибка:'.format(exc))
        else:
            flash('Изменения применены')
        return redirect(url_for('servers'))
    return render_template('add_schedule.html',
                           edit=False,
                           form=form,
                           actions=AppConfig.conf().get('actions'),
                           sorted_actions=sorted(
                               AppConfig.conf().get('actions')))
예제 #11
0
def editSchedule(name):
    form = ScheduleForm()
    if not form.validate_on_submit():
        sch = crontab_manager.get_task(name)
        form['name'].data = name
        form['descr'].data = sch['descr']
        form['cron'].data = sch['cron']
        form['actions'].data = ', '.join(sch['actions'])
    else:
        crontab_manager.update_task(form.name.data, form.cron.data,
                                    form.descr.data, form.actions.data)
        flash('Изменения применены')
        return redirect(url_for('servers'))
    return render_template('add_schedule.html',
                           edit=True,
                           form=form,
                           actions=AppConfig.conf().get('actions'),
                           sorted_actions=sorted(
                               AppConfig.conf().get('actions')))
예제 #12
0
    def get_task(self, name):
        """Возвращает описание задания из конфигурации.

        Args:
            name: Строка, имя задания.

        Returns:
            dict

        """
        return AppConfig.conf().get('schedule').get(name, None)
예제 #13
0
    def update_task(self, name, cron, descr, actions):
        actions = [action.strip() for action in actions.split(',')]

        AppConfig.conf().get('schedule')[name] = {
            'descr': descr,
            'cron': cron,
            'actions': actions,
        }
        AppConfig._config.storeAll()
        if self.is_active(name):
            self.activate_task(name)
예제 #14
0
    def activate_task(self, name):
        """Позволяет активировать одно или все задания.

        Args:
            name (str): имя задания, если имеет значение "all", то
                будут задействованы все задания.

        Raises:
            ConfigErorr: Если отсутствует задание с данным именем.

        """
        if name == 'all':
            schedules = self.get_tasks_with_info()
        else:
            schedule = self.get_task(name)
            if schedule is None:
                raise ConfigError(
                    'Задания с именем {0} отсутствует'.format(name), )
            schedules = {name: schedule}

        for name, schedule in schedules.items():
            crontab = CronTab(user=AppConfig.conf().get('cron').get(
                'cron_user',
                CURRENT_USER,
            ), )
            self._clean_crontab_by_comment(crontab, name)

            cron_time = schedule.get('cron', None)
            if cron_time and not CronSlices.is_valid(cron_time):
                raise ConfigError(
                    'Неверно указано время в задании {0}'.format(name), )

            if schedule.get('all_fields_match', None):
                cron_time = cron_time.split()
                job = crontab.new(
                    command=self._generate_command(
                        name=name,
                        wdays=self._parse_cron_wday_field(cron_time[-1]),
                    ),
                    comment=name,
                )
                cron_time[-1] = '*'
                cron_time = ' '.join(cron_time)
            else:
                job = crontab.new(
                    command=self._generate_command(name),
                    comment=name,
                )

            if (cron_time):
                job.setall(cron_time)
            crontab.write()
예제 #15
0
 def deactivate_task(self, name):
     if name == 'all':
         schedules = self.get_tasks_with_info()
     else:
         schedules = {name: ''}
     for name, _ in schedules.items():
         cron = CronTab(user=AppConfig.conf().get('cron').get(
             'cron_user',
             CURRENT_USER,
         ), )
         for job in cron:
             if job.comment == name:
                 cron.remove(job)
                 cron.write()
예제 #16
0
    def get_tasks_with_info(self):
        """Возвращает словарь с заданиями и информацией о них.

        Returns:
            tasks (dict) с записями вида
                task_name : {'is_active' : bool, \*\*task_configuration}

        """
        tasks = {}
        for taskname in sorted(AppConfig.conf().get('schedule').keys()):
            task_configuration = self.get_task(taskname)
            try:
                task_configuration['active'] = self.is_active(taskname)
            except OSError:
                task_configuration['active'] = None
            tasks[taskname] = task_configuration
        return tasks
예제 #17
0
    def load(self, unit_name, dry):
        """Загружает действия из требуемого задания.

        Если нет, то:
        1) Проверяет наличие задания с именем unit_name.
        2) Если находит: читает его действия и создаёт их.
        Если нет: ищет действие с именем unit_name и создаёт его.

        Args:
            unit_name: Строка, имя требуемого юнита. Если передано действие,
                то оно превращается в задание с одним действием.
            dry: Логическое значение, включает тестовый режим.
                Если True, тоюнит автоматически интерпретируется как действие.

        Raises:
            SystemExit, если не удалось найти необходимый юнит.

        """
        appconf = AppConfig.conf()
        task_record = appconf.setdefault('schedule', {}).get(unit_name)

        if task_record is not None and not dry:
            self.action_records = task_record.get('actions')
            if not self.action_records:
                self.logger.error(
                    'Список действий в задании %s пуст',
                    unit_name,
                )
                sys.exit(-1)
        elif unit_name in appconf.get('actions', {}):
            action_record = [unit_name]
            if dry:
                action_record.append('--dry')
            self.action_records = [action_record]
        elif dry:
            self.logger.error(
                'Отсутствует требуемое действие %s',
                unit_name,
            )
            sys.exit(-1)
        else:
            self.logger.error(
                'Отсутствует требуемое задание/действие %s',
                unit_name,
            )
            sys.exit(-1)
예제 #18
0
 def delete_task(self, name):
     self.deactivate_task(name)
     AppConfig.conf().get('schedule').pop(name, None)
     AppConfig._config.storeAll()
예제 #19
0
def configure_logging(verbose=False):
    """Конфигурирует логирование для выполнения заданий/действий.

    Инициализирует и добавляет хэндлеры для логирования.
    Следующие хэндлеры создаются:
        - log_default - INFO, в файл.
        - log_debug - DEBUG, в файл.
        - trigger - >=WARNING, в триггер файл, если он указан в конфиге.
        - stdout - DEBUG, в консоль, если указан параметр verbose.

    Args:
        verbose: Логическое значение, логировать в stdout.

    """
    try:
        full_name = '{region}-{project}-{servername}'.format(
            region=AppConfig._region,
            project=AppConfig._project,
            servername=AppConfig.get_server_name(),
        )
    except AttributeError:
        full_name = 'created-with-error'

    log_filename = '{full_name}-{time}.log'.format(
        full_name=full_name,
        time=AppConfig.get_starttime_str(),
    )
    log_debug_filename = '{full_name}-{time}-debug.log'.format(
        full_name=full_name,
        time=AppConfig.get_starttime_str(),
    )

    log_path_info = get_log_dirpath(
        subdir=AppConfig.get_starttime().strftime('%Y'), )
    log_path_debug = get_log_dirpath(subdir='debug')

    handlers = [
        get_default_file_handler(
            full_name,
            os.path.join(log_path_info, log_filename),
        ),
        get_debug_file_handler(
            full_name,
            os.path.join(log_path_debug, log_debug_filename),
        ),
    ]

    if verbose or AppConfig.conf().get('logging', {}).get(
            'use_console', False):
        # Инициализация хэндлера консоли, если стоит verbose или use_console.
        handlers.append(get_console_handler(full_name))

    if get_trigger_filepath():
        # Инициализация хэндлера триггера, если к нему указан путь.
        handlers.append(get_trigger_handler())

    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    for log_handler in handlers:
        logger.addHandler(log_handler)