Тип действия batch_playbook в политике

Обработка действия политики типа batch_playbook происходит следующим образом:

  1. Сервис автоматизации создает две темы (subjects) в стриме брокера сообщений NATS:

    • В первую тему записывается информацию об объектах системы, которые необходимо обработать.

    • Во вторую тему скрипт обработки будет записывать информация об объектах, обработанных согласно бизнес-логике.

  2. По завершении записи информации об объектах сервис автоматизации запускает скрипт или плейбук, который должен реализовывать следующие функции:

    1. Чтение данных об объектах из первой темы NATS группами (батчами).

    2. Обработка объектов согласно бизнес-логике.

    3. Запись во вторую тему NATS данных об обработанных объектах.

    4. По завершении обработки всех входных объектов — запись во вторую тему NATS пустого сообщения с заголовком, определенным в поле finishEventHeader действия политики в схеме автоматизации.

  3. Выполнение политики считается завершенным в следующих случаях:

    • Сервис автоматизации прочитал из второй темы NATS сообщение с вышеуказанным заголовком. Запуск политики будет помечен как завершенный успешно.

    • В течение времени, указанного в поле msgTimeout действия политики в схеме автоматизации, в тему данных об обработанных объектах не поступило никаких данных. Запуск политики будет помечен как завершенный с ошибкой.

Ниже приведен пример реализации политики с действием типа batch_playbook и скриптов, реализующих обработку данных согласно вышеуказанному алгоритму.

Использование двух скриптов для выполнения действия типа batch_playbook не является обязательным. Например, вы можете объединить функции работы с NATS и бизнес-логику в один скрипт, который будет реализовывать вышеописанный алгоритм.

Пример настройки политики для выполнения действия типа batch_playbook

Пример политики

Пример представляет собой модификацию политики evo.vulnerability.policy.basic из примера в разделе Схемы автоматизации.

id: evo.vulnerability.policy.batch
type: calculation_schema
name: Политика создания задач на устранение уязвимостей с действием типа batch_playbook
tags: [vulnerabilities, policy, automation]
version: 1.0.0
status: stable
author: John Doe <johndoe@example.com>

policies:
  - id: create_task_if_vulnerability_is_critical
    name: Создать задачу, если уязвимость критическая
    entity: vulnerabilities.rvision.ru/Vulnerability
    conditions:
      - type: expression
        expression: 'equalText({field:criticalityLevel}, "Критический")'
    actions:
      - type: batch_playbook
        playbook: create_tasks_from_critical_vulnerabilities
        msgTimeout: 300000

Интерпретация примера

Схема содержит политику create_task_if_vulnerability_is_critical — автоматическое создание задач для уязвимостей с уровнем Критический:

  • Условие запуска политики типа expression содержит выражение, которое возвращает True, если уровень уязвимости — Критический.

  • Действие политики — запуск скрипта create_tasks_from_critical_vulnerabilities для всех уязвимостей в системе. Скрипт завершает выполнение, если в течение 300 секунд после обработки последней уязвимости в систему не были добавлены новые уязвимости или сервис автоматизации получил из NATS сообщение с заголовком X-Finish-Event.

    Заголовок X-Finish-Event используется по умолчанию. Можно переопределить его, указав свойство finishEventHeader в описании действия например:

    actions:
      - type: batch_playbook
        playbook: create_tasks_from_critical_vulnerabilities
        msgTimeout: 300000
        finishEventHeader: "X-Last-Object"

    Соответствующий заголовок должен быть добавлен в финальное сообщение в скрипте, реализующем работу с NATS.

Пример скрипта, реализующего бизнес-логику

id: create_tasks_from_critical_vulnerabilities
type: script
name: Создать задачи из критических уязвимостей
tags: [vulnerabilities, policy, automation]
version: 1.0.0
status: stable
author: John Doe <johndoe@example.com>

source: !deno |

  import { type Context, executeHandler } from '@expertise/policy_batch_action_handler_executor';
  import { cms } from '@playbooks/sdk';
  import type { PolicyChunkEventResult } from 'npm:@evo.cms/policy-module.contract@1.11.83';
  import type { CmsObject } from 'npm:@evo.cms/policy-module.contract@1.11.83/dist/evo/object_mutation/v1/object_mutation';

  export async function createTasksFromCriticalVulnerabilities(cmsObjects: CmsObject[]): Promise<PolicyChunkEventResult[]> {
    await cms.mass_create({
      domainId: 'tasks.rvision.ru',
      entityId: 'Task',
      valuesList: cmsObjects.map((cmsObject) => {
        const values = JSON.parse(cmsObject.values!);

        return JSON.stringify({
          taskName: "Устранить уязвимость ".concat(values.name),
        });
      }),
    });

    return cmsObjects.map((cmsObject) => ({ policyChunkResult: { id: cmsObject.id } }));
  }

  export async function main(): Promise<void> {
    const context: Context = JSON.parse(get_context());

    await executeHandler({
      handler: createTasksFromCriticalVulnerabilities,
      batchSize: 1000,
      context,
    });
  }

Интерпретация примера

Скрипт содержит следующие функции:

  • Функция createTasksFromCriticalVulnerabilities использует функцию API скриптов для массового создания объектов (задач) в домене Задачи (tasks.rvision.ru). Наименование задачи составляется как Устранить уязвимость <name>, где <name> — наименование уязвимости.

  • Функция main служит точкой входа скрипта и вызывает функцию работы с NATS executeHandler, определенную в скрипте policy_batch_action_handler_executor.

    В функцию работы с NATS должен быть обязательно передан объект контекста выполнения скрипта, полученный с помощью функции get_context. Этот объект содержит имя стрима NATS, а также имена тем для чтения и записи информации об объектах системы.

Пример скрипта, реализующего работу с NATS

id: policy_batch_action_handler_executor
type: script
name: Массовая обработка объектов политики с помощью NATS
tags: [policy, automation]
version: 1.0.0
status: stable
author: John Doe <johndoe@example.com>

source: !deno |

  import type { ConsumerMessages } from 'jsr:@nats-io/jetstream@3.1.0';
  import { jetstream, jetstreamManager } from 'jsr:@nats-io/jetstream@3.1.0';
  import { headers } from 'jsr:@nats-io/nats-core@3.1.0';
  import { connect } from 'jsr:@nats-io/transport-deno@3.1.0';
  import process from 'node:process';
  import {
    PolicyChunkEvent,
    PolicyChunkEventFinish,
    PolicyChunkEventResult,
  } from 'npm:@evo.cms/policy-module.contract@1.11.83';
  import type { CmsObject } from 'npm:@evo.cms/policy-module.contract@1.11.83/dist/evo/object_mutation/v1/object_mutation';

  async function getCmsObjects(messages: ConsumerMessages): Promise<CmsObject[]> {
    const cmsObjects: CmsObject[] = [];

    for await (const message of messages) {
      const policyChunkEvent = PolicyChunkEvent.decode(message.data);

      if (policyChunkEvent.policyChunk && policyChunkEvent.policyChunk.object) {
        cmsObjects.push(policyChunkEvent.policyChunk.object);
      }

      message.ack();
    }

    return cmsObjects;
  }

  export type Context = {
    config: {
      readSubject: string;
      stream: string;
      writeSubject: string;
    };
  };

  export type ExecuteHandlerInput = {
    handler: (cmsObjects: CmsObject[]) => Promise<PolicyChunkEventResult[]>;
    batchSize: number;
    context: Context;
  };

  export async function executeHandler(executeHandlerInput: ExecuteHandlerInput): Promise<void> {
    const nc = await connect({
      servers: [`${process.env.NATS_ADDR}:${process.env.NATS_PORT}`],
    });
    const js = jetstream(nc);
    const jsm = await jetstreamManager(nc);

    const ephemeralConsumerInfo = await jsm.consumers.add(executeHandlerInput.context.config.stream, {
      filter_subject: executeHandlerInput.context.config.readSubject,
      deliver_policy: 'all',
      ack_policy: 'explicit',
    });
    const ephemeralConsumer = await js.consumers.get(
      executeHandlerInput.context.config.stream,
      ephemeralConsumerInfo.name,
    );

    while (true) {
      const messages = await ephemeralConsumer.fetch({ max_messages: executeHandlerInput.batchSize });
      const cmsObjects = await getCmsObjects(messages);

      if (cmsObjects.length == 0) {
        console.log('Стрим NATS полностью прочитан. Завершение чтения...');
        break;
      }

      const policyChunkEventResults = await executeHandlerInput.handler(cmsObjects);

      await Promise.all(
        policyChunkEventResults.map((policyChunkEventResult) =>
          js.publish(
            executeHandlerInput.context.config.writeSubject,
            PolicyChunkEventResult.encode(policyChunkEventResult).finish(),
          ),
        ),
      );
    }

    const h = headers();
    h.set('X-Finish-Event', 'true');

    await js.publish(executeHandlerInput.context.config.writeSubject, PolicyChunkEventFinish.encode({}).finish(), {
      headers: h,
    });
    await nc.close();
  }

Интерпретация примера

Скрипт содержит следующие функции:

  • Функция getCmsObjects реализует чтение входной информации об объектах из NATS, их декодирование и отправку в NATS сообщения об успешном чтении (message.ack();).

  • Типы Context и ExecuteHandlerInput определены для обеспечения корректного формата передачи входных параметров из скрипта create_tasks_from_critical_vulnerabilities.

  • Функция executeHandler состоит из следующих логических блоков:

    1. Подключение к серверу NATS с использованием системных переменных NATS_ADDR и NATS_PORT.

    2. Создание потребителя (consumer) для чтения сообщений из темы входных данных NATS.

    3. Чтение входных объектов из темы NATS в количестве batchSize за одну итерацию и их декодирование. Когда из темы не получено ни одного объекта, цикл прерывается и происходит переход к отправке финального сообщения.

    4. Обработка объектов функцией бизнес-логики executeHandlerInput.handler, переданной в качестве аргумента из скрипта create_tasks_from_critical_vulnerabilities.

    5. Кодирование и запись в тему NATS объектов, полученных в результате работы функции бизнес-логики.

    6. По завершении обработки — создание пустого сообщения NATS с заголовком X-Finish-Event, его запись в тему NATS и закрытие соединения с NATS.

Была ли полезна эта страница?

Обратная связь