Blame SOURCES/022-rhbz1872376.patch

23c78e
From ea636bc7b290325a8d11f56c4ca461d4d010643d Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Mon, 14 Sep 2020 16:29:19 -0500
23c78e
Subject: [PATCH 1/6] Refactor: tools: restructure crm_resource command-line
23c78e
 resource configuration
23c78e
23c78e
... to allow (future) options other than --validate to use the command-line
23c78e
resource configuration options.
23c78e
23c78e
I had planned to use this for a project but went in different direction, so
23c78e
nothing more is expected to use it for now, but I think it's still worthwhile
23c78e
to help isolate different parts of code.
23c78e
---
23c78e
 tools/crm_resource.c | 189 ++++++++++++++++++++++++++-------------------------
23c78e
 1 file changed, 98 insertions(+), 91 deletions(-)
23c78e
23c78e
diff --git a/tools/crm_resource.c b/tools/crm_resource.c
23c78e
index 2fc9a86..1dcb0f0 100644
23c78e
--- a/tools/crm_resource.c
23c78e
+++ b/tools/crm_resource.c
23c78e
@@ -1,5 +1,5 @@
23c78e
 /*
23c78e
- * Copyright 2004-2020 the Pacemaker project contributors
23c78e
+ * Copyright 2004-2021 the Pacemaker project contributors
23c78e
  *
23c78e
  * The version control history for this file may have further details.
23c78e
  *
23c78e
@@ -66,39 +66,46 @@ enum rsc_command {
23c78e
 };
23c78e
 
23c78e
 struct {
23c78e
-    enum rsc_command rsc_cmd;   // The crm_resource command to perform
23c78e
-    const char *attr_set_type;
23c78e
-    int cib_options;
23c78e
-    gboolean clear_expired;
23c78e
-    int find_flags;             /* Flags to use when searching for resource */
23c78e
-    gboolean force;
23c78e
-    gchar *host_uname;
23c78e
-    gchar *interval_spec;
23c78e
-    gchar *move_lifetime;
23c78e
-    gchar *operation;
23c78e
-    GHashTable *override_params;
23c78e
-    gchar *prop_id;
23c78e
-    char *prop_name;
23c78e
-    gchar *prop_set;
23c78e
-    gchar *prop_value;
23c78e
-    gboolean recursive;
23c78e
-    gchar **remainder;
23c78e
-    gboolean require_cib;           // Whether command requires CIB connection
23c78e
-    gboolean require_crmd;          /* whether command requires controller connection */
23c78e
-    gboolean require_dataset;       /* whether command requires populated dataset instance */
23c78e
-    gboolean require_resource;      /* whether command requires that resource be specified */
23c78e
-    int resource_verbose;
23c78e
-    gchar *rsc_id;
23c78e
-    gchar *rsc_type;
23c78e
-    gboolean promoted_role_only;
23c78e
-    int timeout_ms;
23c78e
-    char *agent_spec;               // Standard and/or provider and/or agent
23c78e
-    char *v_agent;
23c78e
-    char *v_class;
23c78e
-    char *v_provider;
23c78e
-    gboolean validate_cmdline;      /* whether we are just validating based on command line options */
23c78e
-    GHashTable *validate_options;
23c78e
-    gchar *xml_file;
23c78e
+    enum rsc_command rsc_cmd;     // crm_resource command to perform
23c78e
+
23c78e
+    // Infrastructure that given command needs to work
23c78e
+    gboolean require_cib;         // Whether command requires CIB IPC
23c78e
+    int cib_options;              // Options to use with CIB IPC calls
23c78e
+    gboolean require_crmd;        // Whether command requires controller IPC
23c78e
+    gboolean require_dataset;     // Whether command requires populated data set
23c78e
+    gboolean require_resource;    // Whether command requires resource specified
23c78e
+    int find_flags;               // Flags to use when searching for resource
23c78e
+
23c78e
+    // Command-line option values
23c78e
+    gchar *rsc_id;                // Value of --resource
23c78e
+    gchar *rsc_type;              // Value of --resource-type
23c78e
+    gboolean force;               // --force was given
23c78e
+    gboolean clear_expired;       // --expired was given
23c78e
+    gboolean recursive;           // --recursive was given
23c78e
+    gboolean promoted_role_only;  // --master was given
23c78e
+    gchar *host_uname;            // Value of --node
23c78e
+    gchar *interval_spec;         // Value of --interval
23c78e
+    gchar *move_lifetime;         // Value of --lifetime
23c78e
+    gchar *operation;             // Value of --operation
23c78e
+    const char *attr_set_type;    // Instance, meta, or utilization attribute
23c78e
+    gchar *prop_id;               // --nvpair (attribute XML ID)
23c78e
+    char *prop_name;              // Attribute name
23c78e
+    gchar *prop_set;              // --set-name (attribute block XML ID)
23c78e
+    gchar *prop_value;            // --parameter-value (attribute value)
23c78e
+    int timeout_ms;               // Parsed from --timeout value
23c78e
+    char *agent_spec;             // Standard and/or provider and/or agent
23c78e
+    gchar *xml_file;              // Value of (deprecated) --xml-file
23c78e
+
23c78e
+    // Resource configuration specified via command-line arguments
23c78e
+    gboolean cmdline_config;      // Resource configuration was via arguments
23c78e
+    char *v_agent;                // Value of --agent
23c78e
+    char *v_class;                // Value of --class
23c78e
+    char *v_provider;             // Value of --provider
23c78e
+    GHashTable *cmdline_params;   // Resource parameters specified
23c78e
+
23c78e
+    // Positional command-line arguments
23c78e
+    gchar **remainder;            // Positional arguments as given
23c78e
+    GHashTable *override_params;  // Resource parameter values that override config
23c78e
 } options = {
23c78e
     .attr_set_type = XML_TAG_ATTR_SETS,
23c78e
     .cib_options = cib_sync_call,
23c78e
@@ -533,28 +540,6 @@ static GOptionEntry advanced_entries[] = {
23c78e
     { NULL }
23c78e
 };
23c78e
 
23c78e
-static GOptionEntry validate_entries[] = {
23c78e
-    { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, class_cb,
23c78e
-      "The standard the resource agent confirms to (for example, ocf).\n"
23c78e
-      INDENT "Use with --agent, --provider, --option, and --validate.",
23c78e
-      "CLASS" },
23c78e
-    { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
23c78e
-      "The agent to use (for example, IPaddr). Use with --class,\n"
23c78e
-      INDENT "--provider, --option, and --validate.",
23c78e
-      "AGENT" },
23c78e
-    { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
23c78e
-      "The vendor that supplies the resource agent (for example,\n"
23c78e
-      INDENT "heartbeat). Use with --class, --agent, --option, and --validate.",
23c78e
-      "PROVIDER" },
23c78e
-    { "option", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, option_cb,
23c78e
-      "Specify a device configuration parameter as NAME=VALUE (may be\n"
23c78e
-      INDENT "specified multiple times). Use with --validate and without the\n"
23c78e
-      INDENT "-r option.",
23c78e
-      "PARAM" },
23c78e
-
23c78e
-    { NULL }
23c78e
-};
23c78e
-
23c78e
 static GOptionEntry addl_entries[] = {
23c78e
     { "node", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.host_uname,
23c78e
       "Node name",
23c78e
@@ -582,6 +567,23 @@ static GOptionEntry addl_entries[] = {
23c78e
     { "interval", 'I', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.interval_spec,
23c78e
       "Interval of operation to clear (default 0) (with -C -r -n)",
23c78e
       "N" },
23c78e
+    { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, class_cb,
23c78e
+      "The standard the resource agent conforms to (for example, ocf).\n"
23c78e
+      INDENT "Use with --agent, --provider, --option, and --validate.",
23c78e
+      "CLASS" },
23c78e
+    { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
23c78e
+      "The agent to use (for example, IPaddr). Use with --class,\n"
23c78e
+      INDENT "--provider, --option, and --validate.",
23c78e
+      "AGENT" },
23c78e
+    { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
23c78e
+      "The vendor that supplies the resource agent (for example,\n"
23c78e
+      INDENT "heartbeat). Use with --class, --agent, --option, and --validate.",
23c78e
+      "PROVIDER" },
23c78e
+    { "option", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, option_cb,
23c78e
+      "Specify a device configuration parameter as NAME=VALUE (may be\n"
23c78e
+      INDENT "specified multiple times). Use with --validate and without the\n"
23c78e
+      INDENT "-r option.",
23c78e
+      "PARAM" },
23c78e
     { "set-name", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_set,
23c78e
       "(Advanced) XML ID of attributes element to use (with -p, -d)",
23c78e
       "ID" },
23c78e
@@ -608,7 +610,7 @@ static GOptionEntry addl_entries[] = {
23c78e
 
23c78e
 gboolean
23c78e
 agent_provider_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
23c78e
-    options.validate_cmdline = TRUE;
23c78e
+    options.cmdline_config = TRUE;
23c78e
     options.require_resource = FALSE;
23c78e
 
23c78e
     if (pcmk__str_eq(option_name, "--provider", pcmk__str_casei)) {
23c78e
@@ -654,7 +656,7 @@ class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **
23c78e
         options.v_class = strdup(optarg);
23c78e
     }
23c78e
 
23c78e
-    options.validate_cmdline = TRUE;
23c78e
+    options.cmdline_config = TRUE;
23c78e
     options.require_resource = FALSE;
23c78e
     return TRUE;
23c78e
 }
23c78e
@@ -762,10 +764,10 @@ option_cb(const gchar *option_name, const gchar *optarg, gpointer data,
23c78e
     if (pcmk_scan_nvpair(optarg, &name, &value) != 2) {
23c78e
         return FALSE;
23c78e
     }
23c78e
-    if (options.validate_options == NULL) {
23c78e
-        options.validate_options = crm_str_table_new();
23c78e
+    if (options.cmdline_params == NULL) {
23c78e
+        options.cmdline_params = crm_str_table_new();
23c78e
     }
23c78e
-    g_hash_table_replace(options.validate_options, name, value);
23c78e
+    g_hash_table_replace(options.cmdline_params, name, value);
23c78e
     return TRUE;
23c78e
 }
23c78e
 
23c78e
@@ -1365,17 +1367,18 @@ show_metadata(pcmk__output_t *out, const char *agent_spec, crm_exit_t *exit_code
23c78e
 }
23c78e
 
23c78e
 static void
23c78e
-validate_cmdline(crm_exit_t *exit_code)
23c78e
+validate_cmdline_config(void)
23c78e
 {
23c78e
-    // -r cannot be used with any of --class, --agent, or --provider
23c78e
+    // Cannot use both --resource and command-line resource configuration
23c78e
     if (options.rsc_id != NULL) {
23c78e
         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
23c78e
                     "--resource cannot be used with --class, --agent, and --provider");
23c78e
 
23c78e
-    // If --class, --agent, or --provider are given, --validate must also be given.
23c78e
+    // Not all commands support command-line resource configuration
23c78e
     } else if (options.rsc_cmd != cmd_execute_agent) {
23c78e
         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
23c78e
-                    "--class, --agent, and --provider require --validate");
23c78e
+                    "--class, --agent, and --provider can only be used with "
23c78e
+                    "--validate");
23c78e
 
23c78e
     // Not all of --class, --agent, and --provider need to be given.  Not all
23c78e
     // classes support the concept of a provider.  Check that what we were given
23c78e
@@ -1398,15 +1401,16 @@ validate_cmdline(crm_exit_t *exit_code)
23c78e
                     options.v_agent ? options.v_agent : "");
23c78e
     }
23c78e
 
23c78e
-    if (error == NULL) {
23c78e
-        if (options.validate_options == NULL) {
23c78e
-            options.validate_options = crm_str_table_new();
23c78e
-        }
23c78e
-        *exit_code = cli_resource_execute_from_params(out, "test", options.v_class, options.v_provider, options.v_agent,
23c78e
-                                                      "validate-all", options.validate_options,
23c78e
-                                                      options.override_params, options.timeout_ms,
23c78e
-                                                      options.resource_verbose, options.force);
23c78e
+    if (error != NULL) {
23c78e
+        return;
23c78e
+    }
23c78e
+
23c78e
+    if (options.cmdline_params == NULL) {
23c78e
+        options.cmdline_params = crm_str_table_new();
23c78e
     }
23c78e
+    options.require_resource = FALSE;
23c78e
+    options.require_dataset = FALSE;
23c78e
+    options.require_cib = FALSE;
23c78e
 }
23c78e
 
23c78e
 static GOptionContext *
23c78e
@@ -1467,8 +1471,6 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
23c78e
                         "Show command help", command_entries);
23c78e
     pcmk__add_arg_group(context, "locations", "Locations:",
23c78e
                         "Show location help", location_entries);
23c78e
-    pcmk__add_arg_group(context, "validate", "Validate:",
23c78e
-                        "Show validate help", validate_entries);
23c78e
     pcmk__add_arg_group(context, "advanced", "Advanced:",
23c78e
                         "Show advanced option help", advanced_entries);
23c78e
     pcmk__add_arg_group(context, "additional", "Additional Options:",
23c78e
@@ -1512,7 +1514,6 @@ main(int argc, char **argv)
23c78e
         goto done;
23c78e
     }
23c78e
 
23c78e
-    options.resource_verbose = args->verbosity;
23c78e
     out->quiet = args->quiet;
23c78e
 
23c78e
     crm_log_args(argc, argv);
23c78e
@@ -1628,15 +1629,15 @@ main(int argc, char **argv)
23c78e
         goto done;
23c78e
     }
23c78e
 
23c78e
-    // Sanity check validating from command line parameters.  If everything checks out,
23c78e
-    // go ahead and run the validation.  This way we don't need a CIB connection.
23c78e
-    if (options.validate_cmdline) {
23c78e
-        validate_cmdline(&exit_code);
23c78e
-        goto done;
23c78e
-    } else if (options.validate_options != NULL) {
23c78e
+    if (options.cmdline_config) {
23c78e
+        /* A resource configuration was given on the command line. Sanity-check
23c78e
+         * the values and set error if they don't make sense.
23c78e
+         */
23c78e
+        validate_cmdline_config();
23c78e
+    } else if (options.cmdline_params != NULL) {
23c78e
         // @COMPAT @TODO error out here when we can break backward compatibility
23c78e
-        g_hash_table_destroy(options.validate_options);
23c78e
-        options.validate_options = NULL;
23c78e
+        g_hash_table_destroy(options.cmdline_params);
23c78e
+        options.cmdline_params = NULL;
23c78e
     }
23c78e
 
23c78e
     if (error != NULL) {
23c78e
@@ -1773,12 +1774,18 @@ main(int argc, char **argv)
23c78e
             break;
23c78e
 
23c78e
         case cmd_execute_agent:
23c78e
-            exit_code = cli_resource_execute(out, rsc, options.rsc_id,
23c78e
-                                             options.operation,
23c78e
-                                             options.override_params,
23c78e
-                                             options.timeout_ms, cib_conn,
23c78e
-                                             data_set, options.resource_verbose,
23c78e
-                                             options.force);
23c78e
+            if (options.cmdline_config) {
23c78e
+                exit_code = cli_resource_execute_from_params(out, "test",
23c78e
+                    options.v_class, options.v_provider, options.v_agent,
23c78e
+                    "validate-all", options.cmdline_params,
23c78e
+                    options.override_params, options.timeout_ms,
23c78e
+                    args->verbosity, options.force);
23c78e
+            } else {
23c78e
+                exit_code = cli_resource_execute(out, rsc, options.rsc_id,
23c78e
+                    options.operation, options.override_params,
23c78e
+                    options.timeout_ms, cib_conn, data_set,
23c78e
+                    args->verbosity, options.force);
23c78e
+            }
23c78e
             break;
23c78e
 
23c78e
         case cmd_colocations:
23c78e
@@ -2038,7 +2045,7 @@ done:
23c78e
         g_hash_table_destroy(options.override_params);
23c78e
     }
23c78e
 
23c78e
-    /* options.validate_options does not need to be destroyed here.  See the
23c78e
+    /* options.cmdline_params does not need to be destroyed here.  See the
23c78e
      * comments in cli_resource_execute_from_params.
23c78e
      */
23c78e
 
23c78e
-- 
23c78e
1.8.3.1
23c78e
23c78e
23c78e
From e140bd1bc35a20f027f054b4575808bd0ef547fc Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Wed, 16 Sep 2020 15:40:16 -0500
23c78e
Subject: [PATCH 2/6] Low: tools: handle required node names better in
23c78e
 crm_resource
23c78e
23c78e
Currently, --fail is the only option that requires a node name to be specified,
23c78e
but generalize the handling so future options can reuse it.
23c78e
23c78e
This also makes the error handling closer to what's done for required resource
23c78e
names, both in error message and exit status.
23c78e
---
23c78e
 tools/crm_resource.c         | 87 ++++++++++++++++++++++++++++----------------
23c78e
 tools/crm_resource_runtime.c |  8 +---
23c78e
 2 files changed, 57 insertions(+), 38 deletions(-)
23c78e
23c78e
diff --git a/tools/crm_resource.c b/tools/crm_resource.c
23c78e
index 1dcb0f0..2717a62 100644
23c78e
--- a/tools/crm_resource.c
23c78e
+++ b/tools/crm_resource.c
23c78e
@@ -74,6 +74,7 @@ struct {
23c78e
     gboolean require_crmd;        // Whether command requires controller IPC
23c78e
     gboolean require_dataset;     // Whether command requires populated data set
23c78e
     gboolean require_resource;    // Whether command requires resource specified
23c78e
+    gboolean require_node;        // Whether command requires node specified
23c78e
     int find_flags;               // Flags to use when searching for resource
23c78e
 
23c78e
     // Command-line option values
23c78e
@@ -774,6 +775,7 @@ option_cb(const gchar *option_name, const gchar *optarg, gpointer data,
23c78e
 gboolean
23c78e
 fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
23c78e
     options.require_crmd = TRUE;
23c78e
+    options.require_node = TRUE;
23c78e
     SET_COMMAND(cmd_fail);
23c78e
     return TRUE;
23c78e
 }
23c78e
@@ -1483,9 +1485,13 @@ main(int argc, char **argv)
23c78e
 {
23c78e
     xmlNode *cib_xml_copy = NULL;
23c78e
     pe_resource_t *rsc = NULL;
23c78e
-
23c78e
+    pe_node_t *node = NULL;
23c78e
     int rc = pcmk_rc_ok;
23c78e
 
23c78e
+    /*
23c78e
+     * Parse command line arguments
23c78e
+     */
23c78e
+
23c78e
     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
23c78e
     GOptionContext *context = NULL;
23c78e
     GOptionGroup *output_group = NULL;
23c78e
@@ -1502,6 +1508,10 @@ main(int argc, char **argv)
23c78e
         goto done;
23c78e
     }
23c78e
 
23c78e
+    /*
23c78e
+     * Set verbosity
23c78e
+     */
23c78e
+
23c78e
     for (int i = 0; i < args->verbosity; i++) {
23c78e
         crm_bump_log_level(argc, argv);
23c78e
     }
23c78e
@@ -1518,9 +1528,9 @@ main(int argc, char **argv)
23c78e
 
23c78e
     crm_log_args(argc, argv);
23c78e
 
23c78e
-    if (options.host_uname) {
23c78e
-        crm_trace("Option host => %s", options.host_uname);
23c78e
-    }
23c78e
+    /*
23c78e
+     * Validate option combinations
23c78e
+     */
23c78e
 
23c78e
     // If the user didn't explicitly specify a command, list resources
23c78e
     if (options.rsc_cmd == cmd_none) {
23c78e
@@ -1634,30 +1644,42 @@ main(int argc, char **argv)
23c78e
          * the values and set error if they don't make sense.
23c78e
          */
23c78e
         validate_cmdline_config();
23c78e
+        if (error != NULL) {
23c78e
+            exit_code = CRM_EX_USAGE;
23c78e
+            goto done;
23c78e
+        }
23c78e
+
23c78e
     } else if (options.cmdline_params != NULL) {
23c78e
         // @COMPAT @TODO error out here when we can break backward compatibility
23c78e
         g_hash_table_destroy(options.cmdline_params);
23c78e
         options.cmdline_params = NULL;
23c78e
     }
23c78e
 
23c78e
-    if (error != NULL) {
23c78e
+    if (options.require_resource && (options.rsc_id == NULL)) {
23c78e
+        rc = ENXIO;
23c78e
+        exit_code = CRM_EX_USAGE;
23c78e
+        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
23c78e
+                    "Must supply a resource id with -r");
23c78e
+        goto done;
23c78e
+    }
23c78e
+    if (options.require_node && (options.host_uname == NULL)) {
23c78e
+        rc = ENXIO;
23c78e
         exit_code = CRM_EX_USAGE;
23c78e
+        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
23c78e
+                    "Must supply a node name with -N");
23c78e
         goto done;
23c78e
     }
23c78e
 
23c78e
+    /*
23c78e
+     * Set up necessary connections
23c78e
+     */
23c78e
+
23c78e
     if (options.force) {
23c78e
         crm_debug("Forcing...");
23c78e
         cib__set_call_options(options.cib_options, crm_system_name,
23c78e
                               cib_quorum_override);
23c78e
     }
23c78e
 
23c78e
-    if (options.require_resource && !options.rsc_id) {
23c78e
-        rc = ENXIO;
23c78e
-        g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
23c78e
-                    "Must supply a resource id with -r");
23c78e
-        goto done;
23c78e
-    }
23c78e
-
23c78e
     if (options.find_flags && options.rsc_id) {
23c78e
         options.require_dataset = TRUE;
23c78e
     }
23c78e
@@ -1700,6 +1722,11 @@ main(int argc, char **argv)
23c78e
         }
23c78e
     }
23c78e
 
23c78e
+    // If user supplied a node name, check whether it exists
23c78e
+    if ((options.host_uname != NULL) && (data_set != NULL)) {
23c78e
+        node = pe_find_node(data_set->nodes, options.host_uname);
23c78e
+    }
23c78e
+
23c78e
     // Establish a connection to the controller if needed
23c78e
     if (options.require_crmd) {
23c78e
         rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
23c78e
@@ -1718,6 +1745,10 @@ main(int argc, char **argv)
23c78e
         }
23c78e
     }
23c78e
 
23c78e
+    /*
23c78e
+     * Handle requested command
23c78e
+     */
23c78e
+
23c78e
     switch (options.rsc_cmd) {
23c78e
         case cmd_list_resources: {
23c78e
             GListPtr all = NULL;
23c78e
@@ -1844,18 +1875,11 @@ main(int argc, char **argv)
23c78e
             break;
23c78e
 
23c78e
         case cmd_why:
23c78e
-            {
23c78e
-                pe_node_t *dest = NULL;
23c78e
-
23c78e
-                if (options.host_uname) {
23c78e
-                    dest = pe_find_node(data_set->nodes, options.host_uname);
23c78e
-                    if (dest == NULL) {
23c78e
-                        rc = pcmk_rc_node_unknown;
23c78e
-                        goto done;
23c78e
-                    }
23c78e
-                }
23c78e
-                out->message(out, "resource-reasons-list", cib_conn, data_set->resources, rsc, dest);
23c78e
-                rc = pcmk_rc_ok;
23c78e
+            if ((options.host_uname != NULL) && (node == NULL)) {
23c78e
+                rc = pcmk_rc_node_unknown;
23c78e
+            } else {
23c78e
+                rc = out->message(out, "resource-reasons-list", cib_conn,
23c78e
+                                  data_set->resources, rsc, node);
23c78e
             }
23c78e
             break;
23c78e
 
23c78e
@@ -1878,15 +1902,10 @@ main(int argc, char **argv)
23c78e
         case cmd_ban:
23c78e
             if (options.host_uname == NULL) {
23c78e
                 rc = ban_or_move(out, rsc, options.move_lifetime, &exit_code);
23c78e
+            } else if (node == NULL) {
23c78e
+                rc = pcmk_rc_node_unknown;
23c78e
             } else {
23c78e
-                pe_node_t *dest = pe_find_node(data_set->nodes,
23c78e
-                                               options.host_uname);
23c78e
-
23c78e
-                if (dest == NULL) {
23c78e
-                    rc = pcmk_rc_node_unknown;
23c78e
-                    goto done;
23c78e
-                }
23c78e
-                rc = cli_resource_ban(out, options.rsc_id, dest->details->uname,
23c78e
+                rc = cli_resource_ban(out, options.rsc_id, node->details->uname,
23c78e
                                       options.move_lifetime, NULL, cib_conn,
23c78e
                                       options.cib_options,
23c78e
                                       options.promoted_role_only);
23c78e
@@ -2002,6 +2021,10 @@ main(int argc, char **argv)
23c78e
             break;
23c78e
     }
23c78e
 
23c78e
+    /*
23c78e
+     * Clean up and exit
23c78e
+     */
23c78e
+
23c78e
 done:
23c78e
     if (rc != pcmk_rc_ok) {
23c78e
         if (rc == pcmk_rc_no_quorum) {
23c78e
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
23c78e
index 3f28c7b..de5e807 100644
23c78e
--- a/tools/crm_resource_runtime.c
23c78e
+++ b/tools/crm_resource_runtime.c
23c78e
@@ -1,5 +1,5 @@
23c78e
 /*
23c78e
- * Copyright 2004-2020 the Pacemaker project contributors
23c78e
+ * Copyright 2004-2021 the Pacemaker project contributors
23c78e
  *
23c78e
  * The version control history for this file may have further details.
23c78e
  *
23c78e
@@ -511,11 +511,7 @@ send_lrm_rsc_op(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, bool do_fail_
23c78e
         return EINVAL;
23c78e
     }
23c78e
 
23c78e
-    if (host_uname == NULL) {
23c78e
-        out->err(out, "Please specify a node name");
23c78e
-        return EINVAL;
23c78e
-
23c78e
-    } else {
23c78e
+    {
23c78e
         pe_node_t *node = pe_find_node(data_set->nodes, host_uname);
23c78e
 
23c78e
         if (node == NULL) {
23c78e
-- 
23c78e
1.8.3.1
23c78e
23c78e
23c78e
From 31bda91470487790d6e17b6f2cbed282bafd11d0 Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Tue, 15 Sep 2020 15:00:53 -0500
23c78e
Subject: [PATCH 3/6] Refactor: libpacemaker: add files for resource-related
23c78e
 API
23c78e
23c78e
---
23c78e
 include/pacemaker-internal.h   |  3 ++-
23c78e
 include/pcmki/Makefile.am      |  3 ++-
23c78e
 include/pcmki/pcmki_resource.h | 14 ++++++++++++++
23c78e
 lib/pacemaker/Makefile.am      |  3 ++-
23c78e
 lib/pacemaker/pcmk_resource.c  | 21 +++++++++++++++++++++
23c78e
 5 files changed, 41 insertions(+), 3 deletions(-)
23c78e
 create mode 100644 include/pcmki/pcmki_resource.h
23c78e
 create mode 100644 lib/pacemaker/pcmk_resource.c
23c78e
23c78e
diff --git a/include/pacemaker-internal.h b/include/pacemaker-internal.h
23c78e
index 2e75d09..bf33f3e 100644
23c78e
--- a/include/pacemaker-internal.h
23c78e
+++ b/include/pacemaker-internal.h
23c78e
@@ -1,5 +1,5 @@
23c78e
 /*
23c78e
- * Copyright 2019 the Pacemaker project contributors
23c78e
+ * Copyright 2019-2021 the Pacemaker project contributors
23c78e
  *
23c78e
  * The version control history for this file may have further details.
23c78e
  *
23c78e
@@ -14,6 +14,7 @@
23c78e
 #  include <pcmki/pcmki_cluster_queries.h>
23c78e
 #  include <pcmki/pcmki_fence.h>
23c78e
 #  include <pcmki/pcmki_output.h>
23c78e
+#  include <pcmki/pcmki_resource.h>
23c78e
 #  include <pcmki/pcmki_sched_allocate.h>
23c78e
 #  include <pcmki/pcmki_sched_notif.h>
23c78e
 #  include <pcmki/pcmki_sched_utils.h>
23c78e
diff --git a/include/pcmki/Makefile.am b/include/pcmki/Makefile.am
23c78e
index 7aa64c7..446c801 100644
23c78e
--- a/include/pcmki/Makefile.am
23c78e
+++ b/include/pcmki/Makefile.am
23c78e
@@ -1,5 +1,5 @@
23c78e
 #
23c78e
-# Copyright 2019 the Pacemaker project contributors
23c78e
+# Copyright 2019-2021 the Pacemaker project contributors
23c78e
 #
23c78e
 # The version control history for this file may have further details.
23c78e
 #
23c78e
@@ -13,6 +13,7 @@ noinst_HEADERS		= pcmki_error.h \
23c78e
 			  pcmki_cluster_queries.h \
23c78e
 			  pcmki_fence.h \
23c78e
 			  pcmki_output.h \
23c78e
+			  pcmki_resource.h \
23c78e
 			  pcmki_sched_allocate.h \
23c78e
 			  pcmki_sched_notif.h \
23c78e
 			  pcmki_sched_utils.h \
23c78e
diff --git a/include/pcmki/pcmki_resource.h b/include/pcmki/pcmki_resource.h
23c78e
new file mode 100644
23c78e
index 0000000..effa945
23c78e
--- /dev/null
23c78e
+++ b/include/pcmki/pcmki_resource.h
23c78e
@@ -0,0 +1,14 @@
23c78e
+/*
23c78e
+ * Copyright 2021 the Pacemaker project contributors
23c78e
+ *
23c78e
+ * The version control history for this file may have further details.
23c78e
+ *
23c78e
+ * This source code is licensed under the GNU Lesser General Public License
23c78e
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
23c78e
+ */
23c78e
+#ifndef PCMKI_RESOURCE__H
23c78e
+#define PCMKI_RESOURCE__H
23c78e
+
23c78e
+#include <crm/common/output_internal.h>
23c78e
+
23c78e
+#endif /* PCMK_RESOURCE__H */
23c78e
diff --git a/lib/pacemaker/Makefile.am b/lib/pacemaker/Makefile.am
23c78e
index 4129ade..760c04a 100644
23c78e
--- a/lib/pacemaker/Makefile.am
23c78e
+++ b/lib/pacemaker/Makefile.am
23c78e
@@ -1,5 +1,5 @@
23c78e
 #
23c78e
-# Copyright 2004-2019 the Pacemaker project contributors
23c78e
+# Copyright 2004-2021 the Pacemaker project contributors
23c78e
 #
23c78e
 # The version control history for this file may have further details.
23c78e
 #
23c78e
@@ -31,6 +31,7 @@ libpacemaker_la_SOURCES	=
23c78e
 libpacemaker_la_SOURCES += pcmk_cluster_queries.c
23c78e
 libpacemaker_la_SOURCES += pcmk_fence.c
23c78e
 libpacemaker_la_SOURCES += pcmk_output.c
23c78e
+libpacemaker_la_SOURCES += pcmk_resource.c
23c78e
 libpacemaker_la_SOURCES	+= pcmk_sched_allocate.c
23c78e
 libpacemaker_la_SOURCES += pcmk_sched_bundle.c
23c78e
 libpacemaker_la_SOURCES += pcmk_sched_clone.c
23c78e
diff --git a/lib/pacemaker/pcmk_resource.c b/lib/pacemaker/pcmk_resource.c
23c78e
new file mode 100644
23c78e
index 0000000..05614fc
23c78e
--- /dev/null
23c78e
+++ b/lib/pacemaker/pcmk_resource.c
23c78e
@@ -0,0 +1,21 @@
23c78e
+/*
23c78e
+ * Copyright 2021 the Pacemaker project contributors
23c78e
+ *
23c78e
+ * The version control history for this file may have further details.
23c78e
+ *
23c78e
+ * This source code is licensed under the GNU General Public License version 2
23c78e
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
23c78e
+ */
23c78e
+
23c78e
+#include <crm_internal.h>
23c78e
+
23c78e
+#include <glib.h>
23c78e
+#include <libxml/tree.h>
23c78e
+
23c78e
+#include <crm/common/mainloop.h>
23c78e
+#include <crm/common/results.h>
23c78e
+#include <crm/common/output_internal.h>
23c78e
+#include <crm/pengine/internal.h>
23c78e
+
23c78e
+#include <pacemaker.h>
23c78e
+#include <pacemaker-internal.h>
23c78e
-- 
23c78e
1.8.3.1
23c78e
23c78e
23c78e
From e45fe95cc6f526ab67ab6f718aa1364861ca525b Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Tue, 15 Sep 2020 15:32:25 -0500
23c78e
Subject: [PATCH 4/6] API: libpacemaker: new API pcmk_resource_digests()
23c78e
23c78e
---
23c78e
 include/pacemaker.h            |  22 +++++++-
23c78e
 include/pcmki/pcmki_resource.h |   7 +++
23c78e
 lib/pacemaker/pcmk_output.c    | 107 +++++++++++++++++++++++++++++++++++-
23c78e
 lib/pacemaker/pcmk_resource.c  | 119 +++++++++++++++++++++++++++++++++++++++++
23c78e
 xml/Makefile.am                |  10 +++-
23c78e
 xml/api/digests-2.6.rng        |  33 ++++++++++++
23c78e
 6 files changed, 293 insertions(+), 5 deletions(-)
23c78e
 create mode 100644 xml/api/digests-2.6.rng
23c78e
23c78e
diff --git a/include/pacemaker.h b/include/pacemaker.h
23c78e
index b2a73cd..51bf585 100644
23c78e
--- a/include/pacemaker.h
23c78e
+++ b/include/pacemaker.h
23c78e
@@ -1,5 +1,5 @@
23c78e
 /*
23c78e
- * Copyright 2019 the Pacemaker project contributors
23c78e
+ * Copyright 2019-2021 the Pacemaker project contributors
23c78e
  *
23c78e
  * The version control history for this file may have further details.
23c78e
  *
23c78e
@@ -20,8 +20,11 @@ extern "C" {
23c78e
  * \ingroup pacemaker
23c78e
  */
23c78e
 
23c78e
-#  include <crm/stonith-ng.h>
23c78e
+#  include <glib.h>
23c78e
 #  include <libxml/tree.h>
23c78e
+#  include <crm/pengine/pe_types.h>
23c78e
+
23c78e
+#  include <crm/stonith-ng.h>
23c78e
 
23c78e
 /*!
23c78e
  * \brief Get controller status
23c78e
@@ -55,6 +58,21 @@ int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
23c78e
  */
23c78e
 int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms);
23c78e
 
23c78e
+/*!
23c78e
+ * \brief Calculate and output resource operation digests
23c78e
+ *
23c78e
+ * \param[out] xml        Where to store XML with result
23c78e
+ * \param[in]  rsc        Resource to calculate digests for
23c78e
+ * \param[in]  node       Node whose operation history should be used
23c78e
+ * \param[in]  overrides  Hash table of configuration parameters to override
23c78e
+ * \param[in]  data_set   Cluster working set (with status)
23c78e
+ *
23c78e
+ * \return Standard Pacemaker return code
23c78e
+ */
23c78e
+int pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc,
23c78e
+                          pe_node_t *node, GHashTable *overrides,
23c78e
+                          pe_working_set_t *data_set);
23c78e
+
23c78e
 #ifdef BUILD_PUBLIC_LIBPACEMAKER
23c78e
 
23c78e
 /*!
23c78e
diff --git a/include/pcmki/pcmki_resource.h b/include/pcmki/pcmki_resource.h
23c78e
index effa945..9d2afb5 100644
23c78e
--- a/include/pcmki/pcmki_resource.h
23c78e
+++ b/include/pcmki/pcmki_resource.h
23c78e
@@ -9,6 +9,13 @@
23c78e
 #ifndef PCMKI_RESOURCE__H
23c78e
 #define PCMKI_RESOURCE__H
23c78e
 
23c78e
+#include <glib.h>
23c78e
+
23c78e
 #include <crm/common/output_internal.h>
23c78e
+#include <crm/pengine/pe_types.h>
23c78e
+
23c78e
+int pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc,
23c78e
+                           pe_node_t *node, GHashTable *overrides,
23c78e
+                           pe_working_set_t *data_set);
23c78e
 
23c78e
 #endif /* PCMK_RESOURCE__H */
23c78e
diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c
23c78e
index 500afd1..bc4b91a 100644
23c78e
--- a/lib/pacemaker/pcmk_output.c
23c78e
+++ b/lib/pacemaker/pcmk_output.c
23c78e
@@ -1,5 +1,5 @@
23c78e
 /*
23c78e
- * Copyright 2019-2020 the Pacemaker project contributors
23c78e
+ * Copyright 2019-2021 the Pacemaker project contributors
23c78e
  *
23c78e
  * The version control history for this file may have further details.
23c78e
  *
23c78e
@@ -12,6 +12,7 @@
23c78e
 #include <crm/common/output_internal.h>
23c78e
 #include <crm/stonith-ng.h>
23c78e
 #include <crm/fencing/internal.h>
23c78e
+#include <crm/pengine/internal.h>
23c78e
 #include <libxml/tree.h>
23c78e
 #include <pacemaker-internal.h>
23c78e
 
23c78e
@@ -539,6 +540,108 @@ crmadmin_node_xml(pcmk__output_t *out, va_list args)
23c78e
     return pcmk_rc_ok;
23c78e
 }
23c78e
 
23c78e
+PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
23c78e
+                  "guint", "op_digest_cache_t *")
23c78e
+static int
23c78e
+digests_text(pcmk__output_t *out, va_list args)
23c78e
+{
23c78e
+    pe_resource_t *rsc = va_arg(args, pe_resource_t *);
23c78e
+    pe_node_t *node = va_arg(args, pe_node_t *);
23c78e
+    const char *task = va_arg(args, const char *);
23c78e
+    guint interval_ms = va_arg(args, guint);
23c78e
+    op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
23c78e
+
23c78e
+    char *action_desc = NULL;
23c78e
+    const char *rsc_desc = "unknown resource";
23c78e
+    const char *node_desc = "unknown node";
23c78e
+
23c78e
+    if (interval_ms != 0) {
23c78e
+        action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
23c78e
+                                        ((task == NULL)? "unknown" : task));
23c78e
+    } else if (pcmk__str_eq(task, "monitor", pcmk__str_none)) {
23c78e
+        action_desc = strdup("probe action");
23c78e
+    } else {
23c78e
+        action_desc = crm_strdup_printf("%s action",
23c78e
+                                        ((task == NULL)? "unknown" : task));
23c78e
+    }
23c78e
+    if ((rsc != NULL) && (rsc->id != NULL)) {
23c78e
+        rsc_desc = rsc->id;
23c78e
+    }
23c78e
+    if ((node != NULL) && (node->details->uname != NULL)) {
23c78e
+        node_desc = node->details->uname;
23c78e
+    }
23c78e
+    out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
23c78e
+                    rsc_desc, action_desc, node_desc);
23c78e
+    free(action_desc);
23c78e
+
23c78e
+    if (digests == NULL) {
23c78e
+        out->list_item(out, NULL, "none");
23c78e
+        out->end_list(out);
23c78e
+        return pcmk_rc_ok;
23c78e
+    }
23c78e
+    if (digests->digest_all_calc != NULL) {
23c78e
+        out->list_item(out, NULL, "%s (all parameters)",
23c78e
+                       digests->digest_all_calc);
23c78e
+    }
23c78e
+    if (digests->digest_secure_calc != NULL) {
23c78e
+        out->list_item(out, NULL, "%s (non-private parameters)",
23c78e
+                       digests->digest_secure_calc);
23c78e
+    }
23c78e
+    if (digests->digest_restart_calc != NULL) {
23c78e
+        out->list_item(out, NULL, "%s (non-reloadable parameters)",
23c78e
+                       digests->digest_restart_calc);
23c78e
+    }
23c78e
+    out->end_list(out);
23c78e
+    return pcmk_rc_ok;
23c78e
+}
23c78e
+
23c78e
+static void
23c78e
+add_digest_xml(xmlNode *parent, const char *type, const char *digest,
23c78e
+               xmlNode *digest_source)
23c78e
+{
23c78e
+    if (digest != NULL) {
23c78e
+        xmlNodePtr digest_xml = create_xml_node(parent, "digest");
23c78e
+
23c78e
+        crm_xml_add(digest_xml, "type", ((type == NULL)? "unspecified" : type));
23c78e
+        crm_xml_add(digest_xml, "hash", digest);
23c78e
+        if (digest_source != NULL) {
23c78e
+            add_node_copy(digest_xml, digest_source);
23c78e
+        }
23c78e
+    }
23c78e
+}
23c78e
+
23c78e
+PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
23c78e
+                  "guint", "op_digest_cache_t *")
23c78e
+static int
23c78e
+digests_xml(pcmk__output_t *out, va_list args)
23c78e
+{
23c78e
+    pe_resource_t *rsc = va_arg(args, pe_resource_t *);
23c78e
+    pe_node_t *node = va_arg(args, pe_node_t *);
23c78e
+    const char *task = va_arg(args, const char *);
23c78e
+    guint interval_ms = va_arg(args, guint);
23c78e
+    op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
23c78e
+
23c78e
+    char *interval_s = crm_strdup_printf("%ums", interval_ms);
23c78e
+    xmlNode *xml = NULL;
23c78e
+
23c78e
+    xml = pcmk__output_create_xml_node(out, "digests",
23c78e
+                                       "resource", crm_str(rsc->id),
23c78e
+                                       "node", crm_str(node->details->uname),
23c78e
+                                       "task", crm_str(task),
23c78e
+                                       "interval", interval_s,
23c78e
+                                       NULL);
23c78e
+    free(interval_s);
23c78e
+    if (digests != NULL) {
23c78e
+        add_digest_xml(xml, "all", digests->digest_all_calc,
23c78e
+                       digests->params_all);
23c78e
+        add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
23c78e
+                       digests->params_secure);
23c78e
+        add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
23c78e
+                       digests->params_restart);
23c78e
+    }
23c78e
+    return pcmk_rc_ok;
23c78e
+}
23c78e
+
23c78e
 static pcmk__message_entry_t fmt_functions[] = {
23c78e
     { "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
23c78e
     { "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
23c78e
@@ -557,6 +660,8 @@ static pcmk__message_entry_t fmt_functions[] = {
23c78e
     { "crmadmin-node-list", "default", crmadmin_node_list },
23c78e
     { "crmadmin-node", "default", crmadmin_node_text },
23c78e
     { "crmadmin-node", "xml", crmadmin_node_xml },
23c78e
+    { "digests", "default", digests_text },
23c78e
+    { "digests", "xml", digests_xml },
23c78e
 
23c78e
     { NULL, NULL, NULL }
23c78e
 };
23c78e
diff --git a/lib/pacemaker/pcmk_resource.c b/lib/pacemaker/pcmk_resource.c
23c78e
index 05614fc..197edf8 100644
23c78e
--- a/lib/pacemaker/pcmk_resource.c
23c78e
+++ b/lib/pacemaker/pcmk_resource.c
23c78e
@@ -9,6 +9,7 @@
23c78e
 
23c78e
 #include <crm_internal.h>
23c78e
 
23c78e
+#include <errno.h>
23c78e
 #include <glib.h>
23c78e
 #include <libxml/tree.h>
23c78e
 
23c78e
@@ -19,3 +20,121 @@
23c78e
 
23c78e
 #include <pacemaker.h>
23c78e
 #include <pacemaker-internal.h>
23c78e
+
23c78e
+// Search path for resource operation history (takes node name and resource ID)
23c78e
+#define XPATH_OP_HISTORY "//" XML_CIB_TAG_STATUS                            \
23c78e
+                         "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \
23c78e
+                         "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES      \
23c78e
+                         "/" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']"
23c78e
+
23c78e
+static xmlNode *
23c78e
+best_op(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
23c78e
+{
23c78e
+    char *xpath = NULL;
23c78e
+    xmlNode *history = NULL;
23c78e
+    xmlNode *best = NULL;
23c78e
+
23c78e
+    // Find node's resource history
23c78e
+    xpath = crm_strdup_printf(XPATH_OP_HISTORY, node->details->uname, rsc->id);
23c78e
+    history = get_xpath_object(xpath, data_set->input, LOG_NEVER);
23c78e
+    free(xpath);
23c78e
+
23c78e
+    // Examine each history entry
23c78e
+    for (xmlNode *lrm_rsc_op = first_named_child(history, XML_LRM_TAG_RSC_OP);
23c78e
+         lrm_rsc_op != NULL; lrm_rsc_op = crm_next_same_xml(lrm_rsc_op)) {
23c78e
+
23c78e
+        const char *digest = crm_element_value(lrm_rsc_op,
23c78e
+                                               XML_LRM_ATTR_RESTART_DIGEST);
23c78e
+        guint interval_ms = 0;
23c78e
+
23c78e
+        crm_element_value_ms(lrm_rsc_op, XML_LRM_ATTR_INTERVAL, &interval_ms);
23c78e
+
23c78e
+        if (pcmk__ends_with(ID(lrm_rsc_op), "_last_failure_0")
23c78e
+            || (interval_ms != 0)) {
23c78e
+
23c78e
+            // Only use last failure or recurring op if nothing else available
23c78e
+            if (best == NULL) {
23c78e
+                best = lrm_rsc_op;
23c78e
+            }
23c78e
+            continue;
23c78e
+        }
23c78e
+
23c78e
+        best = lrm_rsc_op;
23c78e
+        if (digest != NULL) {
23c78e
+            // Any non-recurring action with a restart digest is sufficient
23c78e
+            break;
23c78e
+        }
23c78e
+    }
23c78e
+    return best;
23c78e
+}
23c78e
+
23c78e
+/*!
23c78e
+ * \internal
23c78e
+ * \brief Calculate and output resource operation digests
23c78e
+ *
23c78e
+ * \param[in]  out        Output object
23c78e
+ * \param[in]  rsc        Resource to calculate digests for
23c78e
+ * \param[in]  node       Node whose operation history should be used
23c78e
+ * \param[in]  overrides  Hash table of configuration parameters to override
23c78e
+ * \param[in]  data_set   Cluster working set (with status)
23c78e
+ *
23c78e
+ * \return Standard Pacemaker return code
23c78e
+ */
23c78e
+int
23c78e
+pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc,
23c78e
+                       pe_node_t *node, GHashTable *overrides,
23c78e
+                       pe_working_set_t *data_set)
23c78e
+{
23c78e
+    const char *task = NULL;
23c78e
+    xmlNode *xml_op = NULL;
23c78e
+    op_digest_cache_t *digests = NULL;
23c78e
+    guint interval_ms = 0;
23c78e
+    int rc = pcmk_rc_ok;
23c78e
+
23c78e
+    if ((out == NULL) || (rsc == NULL) || (node == NULL) || (data_set == NULL)) {
23c78e
+        return EINVAL;
23c78e
+    }
23c78e
+    if (rsc->variant != pe_native) {
23c78e
+        // Only primitives get operation digests
23c78e
+        return EOPNOTSUPP;
23c78e
+    }
23c78e
+
23c78e
+    // Find XML of operation history to use
23c78e
+    xml_op = best_op(rsc, node, data_set);
23c78e
+
23c78e
+    // Generate an operation key
23c78e
+    if (xml_op != NULL) {
23c78e
+        task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
23c78e
+        crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
23c78e
+    }
23c78e
+    if (task == NULL) { // Assume start if no history is available
23c78e
+        task = RSC_START;
23c78e
+        interval_ms = 0;
23c78e
+    }
23c78e
+
23c78e
+    // Calculate and show digests
23c78e
+    digests = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
23c78e
+                                    overrides, true, data_set);
23c78e
+    rc = out->message(out, "digests", rsc, node, task, interval_ms, digests);
23c78e
+
23c78e
+    pe__free_digests(digests);
23c78e
+    return rc;
23c78e
+}
23c78e
+
23c78e
+int
23c78e
+pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc,
23c78e
+                      pe_node_t *node, GHashTable *overrides,
23c78e
+                      pe_working_set_t *data_set)
23c78e
+{
23c78e
+    pcmk__output_t *out = NULL;
23c78e
+    int rc = pcmk_rc_ok;
23c78e
+
23c78e
+    rc = pcmk__out_prologue(&out, xml);
23c78e
+    if (rc != pcmk_rc_ok) {
23c78e
+        return rc;
23c78e
+    }
23c78e
+    pcmk__register_lib_messages(out);
23c78e
+    rc = pcmk__resource_digests(out, rsc, node, overrides, data_set);
23c78e
+    pcmk__out_epilogue(out, xml, rc);
23c78e
+    return rc;
23c78e
+}
23c78e
diff --git a/xml/Makefile.am b/xml/Makefile.am
23c78e
index e7b9a51..cb6cfa0 100644
23c78e
--- a/xml/Makefile.am
23c78e
+++ b/xml/Makefile.am
23c78e
@@ -1,5 +1,5 @@
23c78e
 #
23c78e
-# Copyright 2004-2019 the Pacemaker project contributors
23c78e
+# Copyright 2004-2021 the Pacemaker project contributors
23c78e
 #
23c78e
 # The version control history for this file may have further details.
23c78e
 #
23c78e
@@ -50,7 +50,13 @@ version_pairs_last = $(wordlist \
23c78e
 # problems.
23c78e
 
23c78e
 # Names of API schemas that form the choices for pacemaker-result content
23c78e
-API_request_base	= command-output crm_mon crm_resource crmadmin stonith_admin version
23c78e
+API_request_base	= command-output	\
23c78e
+			  crm_mon		\
23c78e
+			  crm_resource		\
23c78e
+			  crmadmin		\
23c78e
+			  digests		\
23c78e
+			  stonith_admin		\
23c78e
+			  version
23c78e
 
23c78e
 # Names of CIB schemas that form the choices for cib/configuration content
23c78e
 CIB_cfg_base		= options nodes resources constraints fencing acls tags alerts
23c78e
diff --git a/xml/api/digests-2.6.rng b/xml/api/digests-2.6.rng
23c78e
new file mode 100644
23c78e
index 0000000..7e843d4
23c78e
--- /dev/null
23c78e
+++ b/xml/api/digests-2.6.rng
23c78e
@@ -0,0 +1,33 @@
23c78e
+
23c78e
+
23c78e
+         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
23c78e
+
23c78e
+    <start>
23c78e
+        <ref name="element-digests"/>
23c78e
+    </start>
23c78e
+
23c78e
+    <define name="element-digests">
23c78e
+        <attribute name="resource"> <text/> </attribute>
23c78e
+        <attribute name="node"> <text/> </attribute>
23c78e
+        <attribute name="task"> <text/> </attribute>
23c78e
+        <attribute name="interval"> <text/> </attribute>
23c78e
+        <zeroOrMore>
23c78e
+            <ref name="element-digest"/>
23c78e
+        </zeroOrMore>
23c78e
+    </define>
23c78e
+
23c78e
+    <define name="element-digest">
23c78e
+        <attribute name="type"> <text/> </attribute>
23c78e
+        <attribute name="hash"> <text/> </attribute>
23c78e
+        <optional>
23c78e
+            <element name="parameters">
23c78e
+                <zeroOrMore>
23c78e
+                    <attribute>
23c78e
+                      <anyName/>
23c78e
+                      <text/>
23c78e
+                    </attribute>
23c78e
+                </zeroOrMore>
23c78e
+            </element>
23c78e
+        </optional>
23c78e
+    </define>
23c78e
+</grammar>
23c78e
-- 
23c78e
1.8.3.1
23c78e
23c78e
23c78e
From 4e726eb67c8eed255ee83706ed13cd7ea31f9864 Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Mon, 14 Sep 2020 16:29:41 -0500
23c78e
Subject: [PATCH 5/6] Feature: tools: add crm_resource --digests option
23c78e
23c78e
This is not particularly useful for end users but can help during development,
23c78e
and can be used by higher-level tools to bypass Pacemaker's configuration
23c78e
change detection (with obvious risks).
23c78e
---
23c78e
 tools/crm_resource.c | 39 ++++++++++++++++++++++++++++++++++++++-
23c78e
 1 file changed, 38 insertions(+), 1 deletion(-)
23c78e
23c78e
diff --git a/tools/crm_resource.c b/tools/crm_resource.c
23c78e
index 2717a62..8c7247a 100644
23c78e
--- a/tools/crm_resource.c
23c78e
+++ b/tools/crm_resource.c
23c78e
@@ -40,6 +40,7 @@ enum rsc_command {
23c78e
     cmd_cts,
23c78e
     cmd_delete,
23c78e
     cmd_delete_param,
23c78e
+    cmd_digests,
23c78e
     cmd_execute_agent,
23c78e
     cmd_fail,
23c78e
     cmd_get_param,
23c78e
@@ -158,6 +159,8 @@ gboolean validate_or_force_cb(const gchar *option_name, const gchar *optarg,
23c78e
                               gpointer data, GError **error);
23c78e
 gboolean restart_cb(const gchar *option_name, const gchar *optarg,
23c78e
                     gpointer data, GError **error);
23c78e
+gboolean digests_cb(const gchar *option_name, const gchar *optarg,
23c78e
+                    gpointer data, GError **error);
23c78e
 gboolean wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
23c78e
 gboolean why_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
23c78e
 
23c78e
@@ -507,6 +510,14 @@ static GOptionEntry advanced_entries[] = {
23c78e
     { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, wait_cb,
23c78e
       "(Advanced) Wait until the cluster settles into a stable state",
23c78e
       NULL },
23c78e
+    { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, digests_cb,
23c78e
+      "(Advanced) Show parameter hashes that Pacemaker uses to detect\n"
23c78e
+      INDENT "configuration changes (only accurate if there is resource\n"
23c78e
+      INDENT "history on the specified node). Required: --resource, --node.\n"
23c78e
+      INDENT "Optional: any NAME=VALUE parameters will be used to override\n"
23c78e
+      INDENT "the configuration (to see what the hash would be with those\n"
23c78e
+      INDENT "changes).",
23c78e
+      NULL },
23c78e
     { "force-demote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
23c78e
       validate_or_force_cb,
23c78e
       "(Advanced) Bypass the cluster and demote a resource on the local\n"
23c78e
@@ -893,7 +904,9 @@ validate_or_force_cb(const gchar *option_name, const gchar *optarg,
23c78e
     }
23c78e
     options.operation = g_strdup(option_name + 2); // skip "--"
23c78e
     options.find_flags = pe_find_renamed|pe_find_anon;
23c78e
-    options.override_params = crm_str_table_new();
23c78e
+    if (options.override_params == NULL) {
23c78e
+        options.override_params = crm_str_table_new();
23c78e
+    }
23c78e
     return TRUE;
23c78e
 }
23c78e
 
23c78e
@@ -907,6 +920,20 @@ restart_cb(const gchar *option_name, const gchar *optarg, gpointer data,
23c78e
 }
23c78e
 
23c78e
 gboolean
23c78e
+digests_cb(const gchar *option_name, const gchar *optarg, gpointer data,
23c78e
+           GError **error)
23c78e
+{
23c78e
+    SET_COMMAND(cmd_digests);
23c78e
+    options.find_flags = pe_find_renamed|pe_find_anon;
23c78e
+    if (options.override_params == NULL) {
23c78e
+        options.override_params = crm_str_table_new();
23c78e
+    }
23c78e
+    options.require_node = TRUE;
23c78e
+    options.require_dataset = TRUE;
23c78e
+    return TRUE;
23c78e
+}
23c78e
+
23c78e
+gboolean
23c78e
 wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
23c78e
     SET_COMMAND(cmd_wait);
23c78e
     options.require_resource = FALSE;
23c78e
@@ -1819,6 +1846,16 @@ main(int argc, char **argv)
23c78e
             }
23c78e
             break;
23c78e
 
23c78e
+        case cmd_digests:
23c78e
+            node = pe_find_node(data_set->nodes, options.host_uname);
23c78e
+            if (node == NULL) {
23c78e
+                rc = pcmk_rc_node_unknown;
23c78e
+            } else {
23c78e
+                rc = pcmk__resource_digests(out, rsc, node,
23c78e
+                                            options.override_params, data_set);
23c78e
+            }
23c78e
+            break;
23c78e
+
23c78e
         case cmd_colocations:
23c78e
             rc = out->message(out, "stacks-constraints", rsc, data_set, false);
23c78e
             break;
23c78e
-- 
23c78e
1.8.3.1
23c78e
23c78e
23c78e
From bb34d07013bed2e71ac9cedb4b1631ad5e2825bf Mon Sep 17 00:00:00 2001
23c78e
From: Ken Gaillot <kgaillot@redhat.com>
23c78e
Date: Mon, 23 Nov 2020 12:17:31 -0600
23c78e
Subject: [PATCH 6/6] Test: cts-cli: add regression tests for crm_resource
23c78e
 --digests
23c78e
23c78e
---
23c78e
 cts/Makefile.am                  |   3 +-
23c78e
 cts/cli/crm_resource_digests.xml | 143 +++++++++++++++++++++++++++++++++++++++
23c78e
 cts/cli/regression.tools.exp     |  34 ++++++++++
23c78e
 cts/cts-cli.in                   |  14 +++-
23c78e
 4 files changed, 192 insertions(+), 2 deletions(-)
23c78e
 create mode 100644 cts/cli/crm_resource_digests.xml
23c78e
23c78e
diff --git a/cts/Makefile.am b/cts/Makefile.am
23c78e
index 5666a9f..de02aed 100644
23c78e
--- a/cts/Makefile.am
23c78e
+++ b/cts/Makefile.am
23c78e
@@ -1,5 +1,5 @@
23c78e
 #
23c78e
-# Copyright 2001-2019 the Pacemaker project contributors
23c78e
+# Copyright 2001-2021 the Pacemaker project contributors
23c78e
 #
23c78e
 # The version control history for this file may have further details.
23c78e
 #
23c78e
@@ -65,6 +65,7 @@ dist_cli_DATA	= cli/constraints.xml \
23c78e
 		  cli/crm_diff_old.xml		\
23c78e
 		  cli/crm_mon.xml		\
23c78e
 		  cli/crm_mon-partial.xml	\
23c78e
+		  cli/crm_resource_digests.xml	\
23c78e
 		  cli/regression.acls.exp	\
23c78e
 		  cli/regression.crm_mon.exp	\
23c78e
 		  cli/regression.dates.exp	\
23c78e
diff --git a/cts/cli/crm_resource_digests.xml b/cts/cli/crm_resource_digests.xml
23c78e
new file mode 100644
23c78e
index 0000000..074ca3d
23c78e
--- /dev/null
23c78e
+++ b/cts/cli/crm_resource_digests.xml
23c78e
@@ -0,0 +1,143 @@
23c78e
+<cib crm_feature_set="3.6.3" validate-with="pacemaker-3.0" epoch="253" num_updates="20" admin_epoch="0" cib-last-written="Sun Nov 22 14:45:16 2020" update-origin="node2" update-client="cibadmin" update-user="root" have-quorum="1" dc-uuid="1">
23c78e
+  <configuration>
23c78e
+    <crm_config>
23c78e
+      <cluster_property_set id="cib-bootstrap-options">
23c78e
+        <nvpair id="cts-stonith-enabled" name="stonith-enabled" value="1"/>
23c78e
+        <nvpair id="cib-bootstrap-options-have-watchdog" name="have-watchdog" value="false"/>
23c78e
+        <nvpair id="cib-bootstrap-options-dc-version" name="dc-version" value="2.0.5"/>
23c78e
+        <nvpair id="cib-bootstrap-options-cluster-infrastructure" name="cluster-infrastructure" value="corosync"/>
23c78e
+      </cluster_property_set>
23c78e
+    </crm_config>
23c78e
+    <nodes>
23c78e
+      <node id="1" uname="node1"/>
23c78e
+      <node id="2" uname="node2"/>
23c78e
+      <node id="3" uname="node3"/>
23c78e
+      <node id="4" uname="node4"/>
23c78e
+      <node id="5" uname="node5"/>
23c78e
+    </nodes>
23c78e
+    <resources>
23c78e
+      <primitive class="stonith" id="Fencing" type="fence_xvm">
23c78e
+        <meta_attributes id="Fencing-meta">
23c78e
+          <nvpair id="Fencing-migration-threshold" name="migration-threshold" value="5"/>
23c78e
+        </meta_attributes>
23c78e
+        <instance_attributes id="Fencing-params">
23c78e
+          <nvpair id="Fencing-key_file" name="key_file" value="/etc/pacemaker/fence_xvm.key"/>
23c78e
+          <nvpair id="Fencing-multicast_address" name="multicast_address" value="239.255.100.100"/>
23c78e
+          <nvpair id="Fencing-pcmk_host_list" name="pcmk_host_list" value="node1 node2 node3 node4 node5"/>
23c78e
+        </instance_attributes>
23c78e
+        <operations>
23c78e
+          <op id="Fencing-monitor-120s" interval="120s" name="monitor" timeout="120s"/>
23c78e
+          <op id="Fencing-stop-0" interval="0" name="stop" timeout="60s"/>
23c78e
+          <op id="Fencing-start-0" interval="0" name="start" timeout="60s"/>
23c78e
+        </operations>
23c78e
+      </primitive>
23c78e
+      <primitive class="ocf" id="rsc1" provider="pacemaker" type="Dummy">
23c78e
+        <instance_attributes id="rsc1-instance_attributes">
23c78e
+          <nvpair id="rsc1-instance_attributes-fake" name="fake" value="1"/>
23c78e
+          <nvpair id="rsc1-instance_attributes-passwd" name="passwd" value="secret"/>
23c78e
+        </instance_attributes>
23c78e
+        <instance_attributes id="rsc1-instance_attributes-node1">
23c78e
+          <nvpair id="rsc1-instance_attributes-fake-node1" name="fake" value="0"/>
23c78e
+          <rule id="rsc1-rule1" score="INFINITY">
23c78e
+            <expression attribute="#uname" id="rsc1-rule1-expr1" operation="eq" value="node1"/>
23c78e
+          </rule>
23c78e
+        </instance_attributes>
23c78e
+        <operations>
23c78e
+          <op id="rsc1-migrate_from-interval-0s" interval="0s" name="migrate_from" timeout="20s"/>
23c78e
+          <op id="rsc1-migrate_to-interval-0s" interval="0s" name="migrate_to" timeout="20s"/>
23c78e
+          <op id="rsc1-monitor-interval-10s" interval="10s" name="monitor" timeout="20s"/>
23c78e
+          <op id="rsc1-reload-interval-0s" interval="0s" name="reload" timeout="20s"/>
23c78e
+          <op id="rsc1-start-interval-0s" interval="0s" name="start" timeout="20s"/>
23c78e
+          <op id="rsc1-stop-interval-0s" interval="0s" name="stop" timeout="20s"/>
23c78e
+        </operations>
23c78e
+      </primitive>
23c78e
+    </resources>
23c78e
+    <constraints>
23c78e
+      <rsc_location id="location-rsc1-node1-INFINITY" node="node1" rsc="rsc1" score="INFINITY"/>
23c78e
+    </constraints>
23c78e
+    <fencing-topology/>
23c78e
+    <op_defaults/>
23c78e
+    <alerts/>
23c78e
+    <rsc_defaults/>
23c78e
+  </configuration>
23c78e
+  <status>
23c78e
+    <node_state id="4" uname="node4" in_ccm="true" crmd="online" crm-debug-origin="do_state_transition" join="member" expected="member">
23c78e
+      <transient_attributes id="4">
23c78e
+        <instance_attributes id="status-4"/>
23c78e
+      </transient_attributes>
23c78e
+      <lrm id="4">
23c78e
+        <lrm_resources>
23c78e
+          <lrm_resource id="rsc1" type="Dummy" class="ocf" provider="pacemaker">
23c78e
+            <lrm_rsc_op id="rsc1_last_0" operation_key="rsc1_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="5:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;5:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node4" call-id="136" rc-code="7" op-status="0" interval="0" last-rc-change="1606076573" last-run="1606076573" exec-time="28" queue-time="0" op-digest="2b1b5ccbabbdb96f3f7edb41b0775563" op-force-restart="  envfile op_sleep passwd state  " op-restart-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8" op-secure-params="  passwd  " op-secure-digest="2b1b5ccbabbdb96f3f7edb41b0775563"/>
23c78e
+          </lrm_resource>
23c78e
+          <lrm_resource id="Fencing" type="fence_xvm" class="stonith">
23c78e
+            <lrm_rsc_op id="Fencing_last_0" operation_key="Fencing_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="43:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;43:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node4" call-id="5" rc-code="7" op-status="0" interval="0" last-rc-change="1606076227" last-run="1606076227" exec-time="2" queue-time="0" op-digest="52e34745a77d95a636428d3b550eb867"/>
23c78e
+          </lrm_resource>
23c78e
+        </lrm_resources>
23c78e
+      </lrm>
23c78e
+    </node_state>
23c78e
+    <node_state id="2" uname="node2" in_ccm="true" crmd="online" crm-debug-origin="do_state_transition" join="member" expected="member">
23c78e
+      <transient_attributes id="2">
23c78e
+        <instance_attributes id="status-2"/>
23c78e
+      </transient_attributes>
23c78e
+      <lrm id="2">
23c78e
+        <lrm_resources>
23c78e
+          <lrm_resource id="rsc1" type="Dummy" class="ocf" provider="pacemaker">
23c78e
+            <lrm_rsc_op id="rsc1_last_0" operation_key="rsc1_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="3:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;3:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node2" call-id="101" rc-code="7" op-status="0" interval="0" last-rc-change="1606076573" last-run="1606076573" exec-time="45" queue-time="0" op-digest="2b1b5ccbabbdb96f3f7edb41b0775563" op-force-restart="  envfile op_sleep passwd state  " op-restart-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8" op-secure-params="  passwd  " op-secure-digest="2b1b5ccbabbdb96f3f7edb41b0775563"/>
23c78e
+          </lrm_resource>
23c78e
+          <lrm_resource id="Fencing" type="fence_xvm" class="stonith">
23c78e
+            <lrm_rsc_op id="Fencing_last_0" operation_key="Fencing_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="15:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;15:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node2" call-id="5" rc-code="7" op-status="0" interval="0" last-rc-change="1606076227" last-run="1606076227" exec-time="4" queue-time="0" op-digest="52e34745a77d95a636428d3b550eb867"/>
23c78e
+          </lrm_resource>
23c78e
+        </lrm_resources>
23c78e
+      </lrm>
23c78e
+    </node_state>
23c78e
+    <node_state id="3" uname="node3" in_ccm="true" crmd="online" crm-debug-origin="do_update_resource" join="member" expected="member">
23c78e
+      <transient_attributes id="3">
23c78e
+        <instance_attributes id="status-3"/>
23c78e
+      </transient_attributes>
23c78e
+      <lrm id="3">
23c78e
+        <lrm_resources>
23c78e
+          <lrm_resource id="rsc1" type="Dummy" class="ocf" provider="pacemaker">
23c78e
+            <lrm_rsc_op id="rsc1_last_0" operation_key="rsc1_stop_0" operation="stop" crm-debug-origin="do_update_resource" crm_feature_set="3.6.3" transition-key="7:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;7:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node3" call-id="121" rc-code="0" op-status="0" interval="0" last-rc-change="1606077916" last-run="1606077916" exec-time="26" queue-time="0" op-digest="c18bfacc816dc3a5a53f23c000e6e57e" op-force-restart="  envfile op_sleep passwd state  " op-restart-digest="5de1fd72a2e7762ed41543231034f6d7" op-secure-params="  passwd  " op-secure-digest="2b1b5ccbabbdb96f3f7edb41b0775563"/>
23c78e
+            <lrm_rsc_op id="rsc1_monitor_10000" operation_key="rsc1_monitor_10000" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="1:52:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;1:52:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node3" call-id="118" rc-code="0" op-status="0" interval="10000" last-rc-change="1606076598" exec-time="20" queue-time="0" op-digest="0b73673404cb867681a3c190ccebcc51" op-secure-params="  passwd  " op-secure-digest="2b1b5ccbabbdb96f3f7edb41b0775563"/>
23c78e
+          </lrm_resource>
23c78e
+          <lrm_resource id="Fencing" type="fence_xvm" class="stonith">
23c78e
+            <lrm_rsc_op id="Fencing_last_0" operation_key="Fencing_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="29:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;29:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node3" call-id="5" rc-code="7" op-status="0" interval="0" last-rc-change="1606076227" last-run="1606076227" exec-time="24" queue-time="0" op-digest="52e34745a77d95a636428d3b550eb867"/>
23c78e
+          </lrm_resource>
23c78e
+        </lrm_resources>
23c78e
+      </lrm>
23c78e
+    </node_state>
23c78e
+    <node_state id="5" uname="node5" in_ccm="true" crmd="online" crm-debug-origin="do_state_transition" join="member" expected="member">
23c78e
+      <transient_attributes id="5">
23c78e
+        <instance_attributes id="status-5"/>
23c78e
+      </transient_attributes>
23c78e
+      <lrm id="5">
23c78e
+        <lrm_resources>
23c78e
+          <lrm_resource id="rsc1" type="Dummy" class="ocf" provider="pacemaker">
23c78e
+            <lrm_rsc_op id="rsc1_last_0" operation_key="rsc1_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="6:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;6:51:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node5" call-id="99" rc-code="7" op-status="0" interval="0" last-rc-change="1606076573" last-run="1606076573" exec-time="27" queue-time="0" op-digest="2b1b5ccbabbdb96f3f7edb41b0775563" op-force-restart="  envfile op_sleep passwd state  " op-restart-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8" op-secure-params="  passwd  " op-secure-digest="2b1b5ccbabbdb96f3f7edb41b0775563"/>
23c78e
+          </lrm_resource>
23c78e
+          <lrm_resource id="Fencing" type="fence_xvm" class="stonith">
23c78e
+            <lrm_rsc_op id="Fencing_last_0" operation_key="Fencing_monitor_0" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="57:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:7;57:0:7:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node5" call-id="5" rc-code="7" op-status="0" interval="0" last-rc-change="1606076227" last-run="1606076227" exec-time="14" queue-time="0" op-digest="52e34745a77d95a636428d3b550eb867"/>
23c78e
+          </lrm_resource>
23c78e
+        </lrm_resources>
23c78e
+      </lrm>
23c78e
+    </node_state>
23c78e
+    <node_state id="1" uname="node1" in_ccm="true" crmd="online" crm-debug-origin="do_update_resource" join="member" expected="member">
23c78e
+      <transient_attributes id="1">
23c78e
+        <instance_attributes id="status-1"/>
23c78e
+      </transient_attributes>
23c78e
+      <lrm id="1">
23c78e
+        <lrm_resources>
23c78e
+          <lrm_resource id="rsc1" type="Dummy" class="ocf" provider="pacemaker">
23c78e
+            <lrm_rsc_op id="rsc1_last_0" operation_key="rsc1_start_0" operation="start" crm-debug-origin="do_update_resource" crm_feature_set="3.6.3" transition-key="8:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;8:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node1" call-id="104" rc-code="0" op-status="0" interval="0" last-rc-change="1606077916" last-run="1606077916" exec-time="22" queue-time="0" op-digest="3acdbe4c12734ebeb1251a59545af936" op-force-restart="  envfile op_sleep passwd state  " op-restart-digest="5de1fd72a2e7762ed41543231034f6d7" op-secure-params="  passwd  " op-secure-digest="279c477dbc38c621904a00ab9e599b2f"/>
23c78e
+            <lrm_rsc_op id="rsc1_monitor_10000" operation_key="rsc1_monitor_10000" operation="monitor" crm-debug-origin="do_update_resource" crm_feature_set="3.6.3" transition-key="9:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;9:55:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node1" call-id="106" rc-code="0" op-status="0" interval="10000" last-rc-change="1606077916" exec-time="20" queue-time="0" op-digest="720718e8d715d5d3be1403cbbcb953bc" op-secure-params="  passwd  " op-secure-digest="279c477dbc38c621904a00ab9e599b2f"/>
23c78e
+          </lrm_resource>
23c78e
+          <lrm_resource id="Fencing" type="fence_xvm" class="stonith">
23c78e
+            <lrm_rsc_op id="Fencing_last_0" operation_key="Fencing_start_0" operation="start" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="71:0:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;71:0:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node1" call-id="10" rc-code="0" op-status="0" interval="0" last-rc-change="1606076227" last-run="1606076227" exec-time="59" queue-time="0" op-digest="52e34745a77d95a636428d3b550eb867"/>
23c78e
+            <lrm_rsc_op id="Fencing_monitor_120000" operation_key="Fencing_monitor_120000" operation="monitor" crm-debug-origin="build_active_RAs" crm_feature_set="3.6.3" transition-key="72:0:0:727e4004-8c04-423b-8d63-65ae1fabd119" transition-magic="0:0;72:0:0:727e4004-8c04-423b-8d63-65ae1fabd119" exit-reason="" on_node="node1" call-id="12" rc-code="0" op-status="0" interval="120000" last-rc-change="1606076227" exec-time="70" queue-time="0" op-digest="acc6dd2c58c637db4d12a6fe35626617"/>
23c78e
+          </lrm_resource>
23c78e
+        </lrm_resources>
23c78e
+      </lrm>
23c78e
+    </node_state>
23c78e
+  </status>
23c78e
+</cib>
23c78e
diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
23c78e
index a85b7d6..510cc0a 100644
23c78e
--- a/cts/cli/regression.tools.exp
23c78e
+++ b/cts/cli/regression.tools.exp
23c78e
@@ -4019,3 +4019,37 @@ Resources colocated with clone:
23c78e
 </pacemaker-result>
23c78e
 =#=#=#= End test: Recursively check locations and constraints for clone in XML - OK (0) =#=#=#=
23c78e
 * Passed: crm_resource   - Recursively check locations and constraints for clone in XML
23c78e
+=#=#=#= Begin test: Show resource digests =#=#=#=
23c78e
+<pacemaker-result api-version="X" request="crm_resource --digests -r rsc1 -N node1 --output-as=xml">
23c78e
+  <digests resource="rsc1" node="node1" task="start" interval="0ms">
23c78e
+    <digest type="all" hash="3acdbe4c12734ebeb1251a59545af936">
23c78e
+      <parameters passwd="secret" fake="0"/>
23c78e
+    </digest>
23c78e
+    <digest type="nonprivate" hash="279c477dbc38c621904a00ab9e599b2f">
23c78e
+      <parameters fake="0"/>
23c78e
+    </digest>
23c78e
+    <digest type="nonreloadable" hash="5de1fd72a2e7762ed41543231034f6d7">
23c78e
+      <parameters passwd="secret"/>
23c78e
+    </digest>
23c78e
+  </digests>
23c78e
+  <status code="0" message="OK"/>
23c78e
+</pacemaker-result>
23c78e
+=#=#=#= End test: Show resource digests - OK (0) =#=#=#=
23c78e
+* Passed: crm_resource   - Show resource digests
23c78e
+=#=#=#= Begin test: Show resource digests with overrides =#=#=#=
23c78e
+<pacemaker-result api-version="X" request="crm_resource --digests -r rsc1 -N node1 --output-as=xml CRM_meta_interval=10000 CRM_meta_timeout=20000">
23c78e
+  <digests resource="rsc1" node="node1" task="start" interval="10000ms">
23c78e
+    <digest type="all" hash="720718e8d715d5d3be1403cbbcb953bc">
23c78e
+      <parameters passwd="secret" fake="0" CRM_meta_timeout="20000"/>
23c78e
+    </digest>
23c78e
+    <digest type="nonprivate" hash="279c477dbc38c621904a00ab9e599b2f">
23c78e
+      <parameters fake="0"/>
23c78e
+    </digest>
23c78e
+    <digest type="nonreloadable" hash="5de1fd72a2e7762ed41543231034f6d7">
23c78e
+      <parameters passwd="secret"/>
23c78e
+    </digest>
23c78e
+  </digests>
23c78e
+  <status code="0" message="OK"/>
23c78e
+</pacemaker-result>
23c78e
+=#=#=#= End test: Show resource digests with overrides - OK (0) =#=#=#=
23c78e
+* Passed: crm_resource   - Show resource digests with overrides
23c78e
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
23c78e
index dfdd3de..96f5386 100755
23c78e
--- a/cts/cts-cli.in
23c78e
+++ b/cts/cts-cli.in
23c78e
@@ -1,6 +1,6 @@
23c78e
 #!@BASH_PATH@
23c78e
 #
23c78e
-# Copyright 2008-2020 the Pacemaker project contributors
23c78e
+# Copyright 2008-2021 the Pacemaker project contributors
23c78e
 #
23c78e
 # The version control history for this file may have further details.
23c78e
 #
23c78e
@@ -791,6 +791,18 @@ function test_tools() {
23c78e
     done
23c78e
 
23c78e
     unset CIB_file
23c78e
+
23c78e
+    export CIB_file="$test_home/cli/crm_resource_digests.xml"
23c78e
+
23c78e
+    desc="Show resource digests"
23c78e
+    cmd="crm_resource --digests -r rsc1 -N node1 --output-as=xml"
23c78e
+    test_assert $CRM_EX_OK 0
23c78e
+
23c78e
+    desc="Show resource digests with overrides"
23c78e
+    cmd="$cmd CRM_meta_interval=10000 CRM_meta_timeout=20000"
23c78e
+    test_assert $CRM_EX_OK 0
23c78e
+
23c78e
+    unset CIB_file
23c78e
 }
23c78e
 
23c78e
 INVALID_PERIODS=(
23c78e
-- 
23c78e
1.8.3.1
23c78e