/*
 * Copyright 2004-2026 the Pacemaker project contributors
 *
 * The version control history for this file may have further details.
 *
 * This source code is licensed under the GNU Lesser General Public License
 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
 */

#ifndef PCMK__INCLUDED_CRM_COMMON_INTERNAL_H
#error "Include <crm/common/internal.h> instead of <scheduler_internal.h> directly"
#endif

#ifndef PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H
#define PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H

#include <inttypes.h>                          // UINT64_C
#include <crm/common/action_relation_internal.h>
#include <crm/common/actions_internal.h>
#include <crm/common/attrs_internal.h>
#include <crm/common/bundles_internal.h>
#include <crm/common/clone_internal.h>
#include <crm/common/digest_internal.h>
#include <crm/common/failcounts_internal.h>
#include <crm/common/group_internal.h>
#include <crm/common/history_internal.h>
#include <crm/common/location_internal.h>
#include <crm/common/nodes_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/primitive_internal.h>
#include <crm/common/remote_internal.h>
#include <crm/common/resources_internal.h>
#include <crm/common/roles_internal.h>
#include <crm/common/rules_internal.h>
#include <crm/common/tickets_internal.h>

#ifdef __cplusplus
extern "C" {
#endif

enum pcmk__check_parameters {
    /* Clear fail count if parameters changed for un-expired start or monitor
     * last_failure.
     */
    pcmk__check_last_failure,

    /* Clear fail count if parameters changed for start, monitor, promote, or
     * migrate_from actions for active resources.
     */
    pcmk__check_active,
};

// Scheduling options and conditions
enum pcmk__scheduler_flags {
    // No scheduler flags set (compare with equality rather than bit set)
    pcmk__sched_none                    = UINT64_C(0),

    /* These flags are dynamically determined conditions */

    // Whether partition has quorum (via \c PCMK_XA_HAVE_QUORUM attribute)
    //! \deprecated Call pcmk_has_quorum() to check quorum instead
    pcmk__sched_quorate                 = (UINT64_C(1) << 0),

    // Whether cluster is symmetric (via symmetric-cluster property)
    pcmk__sched_symmetric_cluster       = (UINT64_C(1) << 1),

    // Whether scheduling encountered a non-configuration error
    pcmk__sched_processing_error        = (UINT64_C(1) << 2),

    // Whether cluster is in maintenance mode (via maintenance-mode property)
    pcmk__sched_in_maintenance          = (UINT64_C(1) << 3),

    // Whether fencing is enabled (via \c PCMK_OPT_FENCING_ENABLED property)
    pcmk__sched_fencing_enabled         = (UINT64_C(1) << 4),

    // Whether cluster has a fencing resource (via CIB resources)
    /*! \deprecated To indicate the cluster has a fencing resource, add either a
     * fencing resource configuration or the have-watchdog cluster option to the
     * input CIB
     */
    pcmk__sched_have_fencing            = (UINT64_C(1) << 5),

    // Whether any resource provides or requires unfencing (via CIB resources)
    pcmk__sched_enable_unfencing        = (UINT64_C(1) << 6),

    /* Whether concurrent fencing is allowed (via PCMK__OPT_CONCURRENT_FENCING
     * property).
     *
     * @COMPAT The PCMK__OPT_CONCURRENT_FENCING property is deprecated.
     */
    pcmk__sched_concurrent_fencing      = (UINT64_C(1) << 7),

    /* Whether resources removed from the configuration should be stopped (via
     * PCMK__OPT_STOP_REMOVED_RESOURCES property)
     *
     * @COMPAT The PCMK__OPT_STOP_REMOVED_RESOURCES property is deprecated.
     */
    pcmk__sched_stop_removed_resources  = (UINT64_C(1) << 8),

    /* Whether recurring actions removed from the configuration should be
     * cancelled (via PCMK__OPT_CANCEL_REMOVED_ACTIONS property)
     *
     * @COMPAT The PCMK__OPT_CANCEL_REMOVED_ACTIONS property is deprecated.
     */
    pcmk__sched_cancel_removed_actions  = (UINT64_C(1) << 9),

    // Whether to stop all resources (via stop-all-resources property)
    pcmk__sched_stop_all                = (UINT64_C(1) << 10),

    // Whether scheduler processing encountered a warning
    pcmk__sched_processing_warning      = (UINT64_C(1) << 11),

    /*
     * Whether start failure should be treated as if
     * \c PCMK_META_MIGRATION_THRESHOLD is 1 (via
     * \c PCMK_OPT_START_FAILURE_IS_FATAL property)
     */
    pcmk__sched_start_failure_fatal     = (UINT64_C(1) << 12),

    // Whether unseen nodes should be fenced (via startup-fencing property)
    pcmk__sched_startup_fencing         = (UINT64_C(1) << 14),

    /*
     * Whether resources should be left stopped when their node shuts down
     * cleanly (via shutdown-lock property)
     */
    pcmk__sched_shutdown_lock           = (UINT64_C(1) << 15),

    /*
     * Whether resources' current state should be probed (when unknown) before
     * scheduling any other actions (via the PCMK__OPT_ENABLE_STARTUP_PROBES
     * property).
     *
     * @COMPAT Drop this when the PCMK__OPT_ENABLE_STARTUP_PROBES property is
     * dropped.
     */
    pcmk__sched_probe_resources         = (UINT64_C(1) << 16),

    // Whether the CIB status section has been parsed yet
    pcmk__sched_have_status             = (UINT64_C(1) << 17),

    // Whether the cluster includes any Pacemaker Remote nodes (via CIB)
    pcmk__sched_have_remote_nodes       = (UINT64_C(1) << 18),


    /* The remaining flags are scheduling options that must be set explicitly */

    /*
     * Whether to skip unpacking the CIB status section and stop the scheduling
     * sequence after applying node-specific location criteria (skipping
     * assignment, ordering, actions, etc.).
     */
    pcmk__sched_location_only           = (UINT64_C(1) << 20),

    // Whether sensitive resource attributes have been masked
    pcmk__sched_sanitized               = (UINT64_C(1) << 21),

    // Skip counting of total, disabled, and blocked resource instances
    pcmk__sched_no_counts               = (UINT64_C(1) << 23),

    // Whether node scores should be output instead of logged
    pcmk__sched_output_scores           = (UINT64_C(1) << 25),

    // Whether to show node and resource utilization (in log or output)
    pcmk__sched_show_utilization        = (UINT64_C(1) << 26),

    /*
     * Whether to stop the scheduling sequence after unpacking the CIB,
     * calculating cluster status, and applying node health (skipping
     * applying node-specific location criteria, assignment, etc.)
     */
    pcmk__sched_validate_only           = (UINT64_C(1) << 27),

    /* Can Pacemaker Remote nodes be fenced even from a node that doesn't
     * have quorum?
     */
    pcmk__sched_fence_remote_no_quorum  = (UINT64_C(1) << 28),
};

// Implementation of pcmk__scheduler_private_t
struct pcmk__scheduler_private {
    // Be careful about when each piece of information is available and final

    char *local_node_name;          // Name of node running scheduler (if known)
    crm_time_t *now;                // Time to use when evaluating rules
    pcmk__output_t *out;            // Output object for displaying messages
    GHashTable *options;            // Cluster options
    const char *fence_action;       // Default fencing action
    guint fence_timeout_ms;         // Default fencing action timeout (in ms)
    guint priority_fencing_ms;      // Priority-based fencing delay (in ms)
    guint shutdown_lock_ms;         // How long to lock resources (in ms)
    guint node_pending_ms;          // Pending join times out after this (in ms)

    // @TODO convert to enum
    const char *placement_strategy; // Value of placement-strategy property

    xmlNode *rsc_defaults;          // Configured resource defaults
    xmlNode *op_defaults;           // Configured operation defaults
    GList *resources;               // Resources in cluster
    GHashTable *templates;          // Key = template ID, value = resource list
    GHashTable *tags;               // Key = tag ID, value = element list
    GList *actions;                 // All scheduled actions
    GHashTable *singletons;         // Scheduled non-resource actions
    int next_action_id;             // Counter used as ID for actions
    xmlNode *failed;                // History entries of failed actions
    GList *param_check;             // History entries that need to be checked
    GList *stop_needed;             // Containers that need stop actions
    GList *location_constraints;    // Location constraints
    GList *colocation_constraints;  // Colocation constraints
    GList *ordering_constraints;    // Ordering constraints
    GHashTable *ticket_constraints; // Key = ticket ID, value = pcmk__ticket_t
    int next_ordering_id;           // Counter used as ID for orderings
    int ninstances;                 // Total number of resource instances
    int blocked_resources;          // Number of blocked resources in cluster
    int disabled_resources;         // Number of disabled resources in cluster
    time_t recheck_by;              // Hint to controller when to reschedule
    xmlNode *graph;                 // Transition graph
    int synapse_count;              // Number of transition graph synapses
};

// Group of enum pcmk__warnings flags for warnings we want to log once
extern uint32_t pcmk__warnings;

/*!
 * \internal
 * \brief Log a resource-tagged message at info severity
 *
 * \param[in] rsc       Tag message with this resource's ID
 * \param[in] fmt...    printf(3)-style format and arguments
 */
#define pcmk__rsc_info(rsc, fmt, args...)   \
    crm_log_tag(LOG_INFO, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)

/*!
 * \internal
 * \brief Log a resource-tagged message at debug severity
 *
 * \param[in] rsc       Tag message with this resource's ID
 * \param[in] fmt...    printf(3)-style format and arguments
 */
#define pcmk__rsc_debug(rsc, fmt, args...)  \
    crm_log_tag(LOG_DEBUG, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)

/*!
 * \internal
 * \brief Log a resource-tagged message at trace severity
 *
 * \param[in] rsc       Tag message with this resource's ID
 * \param[in] fmt...    printf(3)-style format and arguments
 */
#define pcmk__rsc_trace(rsc, fmt, args...)  \
    crm_log_tag(LOG_TRACE, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)

/*!
 * \internal
 * \brief Log an error and remember that current scheduler input has errors
 *
 * \param[in,out] scheduler  Scheduler data
 * \param[in]     fmt...     printf(3)-style format and arguments
 */
#define pcmk__sched_err(scheduler, fmt...) do {                     \
        pcmk__set_scheduler_flags((scheduler),                      \
                                  pcmk__sched_processing_error);    \
        pcmk__err(fmt);                                             \
    } while (0)

/*!
 * \internal
 * \brief Log a warning and remember that current scheduler input has warnings
 *
 * \param[in,out] scheduler  Scheduler data
 * \param[in]     fmt...     printf(3)-style format and arguments
 */
#define pcmk__sched_warn(scheduler, fmt...) do {                    \
        pcmk__set_scheduler_flags((scheduler),                      \
                                  pcmk__sched_processing_warning);  \
        pcmk__warn(fmt);                                            \
    } while (0)

/*!
 * \internal
 * \brief Set scheduler flags
 *
 * \param[in,out] scheduler     Scheduler data
 * \param[in]     flags_to_set  Group of enum pcmk__scheduler_flags to set
 */
#define pcmk__set_scheduler_flags(scheduler, flags_to_set) do {             \
        (scheduler)->flags = pcmk__set_flags_as(__func__, __LINE__,         \
            LOG_TRACE, "Scheduler", crm_system_name,                        \
            (scheduler)->flags, (flags_to_set), #flags_to_set);             \
    } while (0)

/*!
 * \internal
 * \brief Clear scheduler flags
 *
 * \param[in,out] scheduler       Scheduler data
 * \param[in]     flags_to_clear  Group of enum pcmk__scheduler_flags to clear
 */
#define pcmk__clear_scheduler_flags(scheduler, flags_to_clear) do {         \
        (scheduler)->flags = pcmk__clear_flags_as(__func__, __LINE__,       \
            LOG_TRACE, "Scheduler", crm_system_name,                        \
            (scheduler)->flags, (flags_to_clear), #flags_to_clear);         \
    } while (0)

void pcmk__set_scheduler_defaults(pcmk_scheduler_t *scheduler);
time_t pcmk__scheduler_epoch_time(pcmk_scheduler_t *scheduler);
void pcmk__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler,
                               const char *reason);

void pcmk__add_param_check(const xmlNode *rsc_op, pcmk_resource_t *rsc,
                           pcmk_node_t *node, enum pcmk__check_parameters);
void pcmk__foreach_param_check(pcmk_scheduler_t *scheduler,
                               void (*cb)(pcmk_resource_t*, pcmk_node_t*,
                                          const xmlNode*,
                                          enum pcmk__check_parameters));
void pcmk__free_param_checks(pcmk_scheduler_t *scheduler);

#ifdef __cplusplus
}
#endif

#endif // PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H
