diff --git a/.gitignore b/.gitignore index 85dcf9149a..e92b7debd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,213 +1,212 @@ # Common \#* .\#* GPATH GRTAGS GTAGS TAGS Makefile Makefile.in .deps .dirstamp .libs *.pc *.pyc *.bz2 *.tar.gz *.rpm *.la *.lo *.o *~ *.gcda *.gcno # Autobuild aclocal.m4 autoconf autoheader autom4te.cache/ automake build.counter compile config.guess config.log config.status config.sub configure depcomp install-sh include/stamp-* libtool libtool.m4 ltdl.m4 libltdl ltmain.sh missing py-compile /m4/argz.m4 /m4/ltargz.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 test-driver ylwrap # Configure targets Doxyfile /cts/CTS.py /cts/CTSlab.py /cts/CTSvars.py /cts/LSBDummy /cts/OCFIPraTest.py /cts/benchmark/clubench /cts/cluster_test /cts/cts /cts/cts-cli /cts/cts-coverage /cts/cts-exec /cts/cts-fencing /cts/cts-log-watcher /cts/cts-regression /cts/cts-scheduler /cts/cts-support /cts/fence_dummy /cts/lxc_autogen.sh /cts/pacemaker-cts-dummyd /cts/pacemaker-cts-dummyd@.service /daemons/execd/pacemaker_remote /daemons/execd/pacemaker_remote.service /daemons/fenced/fence_legacy /daemons/pacemakerd/pacemaker /daemons/pacemakerd/pacemaker.combined.upstart /daemons/pacemakerd/pacemaker.service /daemons/pacemakerd/pacemaker.upstart /extra/logrotate/pacemaker /extra/resources/ClusterMon /extra/resources/HealthSMART /extra/resources/SysInfo /extra/resources/ifspeed /extra/resources/o2cb include/config.h include/config.h.in include/crm_config.h publican.cfg /tools/cibsecret /tools/crm_error /tools/crm_failcount /tools/crm_master /tools/crm_mon.service /tools/crm_mon.upstart /tools/crm_report /tools/crm_standby /tools/report.collector /tools/report.common # Build targets *.7 *.7.xml *.7.html *.8 *.8.xml *.8.html doc/*/en-US/images/*.png doc/*/tmp/** doc/*/publish /daemons/attrd/pacemaker-attrd /daemons/based/pacemaker-based /daemons/based/cibmon /daemons/controld/pacemaker-controld /daemons/execd/cts-exec-helper /daemons/execd/pacemaker-execd /daemons/execd/pacemaker-remoted /daemons/fenced/cts-fence-helper /daemons/fenced/pacemaker-fenced /daemons/fenced/pacemaker-fenced.xml /daemons/pacemakerd/pacemakerd /daemons/schedulerd/pacemaker-schedulerd /daemons/schedulerd/pacemaker-schedulerd.xml -/daemons/schedulerd/ptest doc/api/* doc/Clusters_from_Scratch.txt doc/Pacemaker_Explained.txt doc/acls.html doc/crm_fencing.html doc/publican-catalog* scratch /tools/attrd_updater /tools/cibadmin /tools/crmadmin /tools/crm_attribute /tools/crm_diff /tools/crm_mon /tools/crm_node /tools/crm_resource /tools/crm_shadow /tools/crm_simulate /tools/crm_ticket /tools/crm_verify /tools/iso8601 /tools/stonith_admin xml/crm.dtd xml/pacemaker*.rng xml/versions.rng doc/shared/en-US/*.xml doc/Clusters_from_Scratch.build doc/Clusters_from_Scratch/en-US/Ap-*.xml doc/Clusters_from_Scratch/en-US/Ch-*.xml doc/Pacemaker_Administration.build doc/Pacemaker_Administration/en-US/Ch-*.xml doc/Pacemaker_Development.build doc/Pacemaker_Development/en-US/Ch-*.xml doc/Pacemaker_Explained.build doc/Pacemaker_Explained/en-US/Ch-*.xml doc/Pacemaker_Explained/en-US/Ap-*.xml doc/Pacemaker_Remote.build doc/Pacemaker_Remote/en-US/Ch-*.xml lib/gnu/libgnu.a lib/gnu/stdalign.h *.coverity # Test detritus /cts/.regression.failed.diff /cts/scheduler/*.ref /cts/scheduler/*.up /cts/scheduler/*.up.err /cts/scheduler/bug-rh-1097457.log /cts/scheduler/bug-rh-1097457.trs /cts/scheduler/shadow.* /cts/test-suite.log /xml/test-*/*.up /xml/test-*/*.up.err /xml/assets/diffview.js # Formerly built files (helps when jumping back and forth in checkout) /attrd /cib /coverage.sh /crmd /cts/HBDummy /fencing /lrmd /mcp /pengine #Other mock HTML pacemaker*.spec coverity-* compat_reports .ABI-build abi_dumps logs *.patch *.diff *.sed *.orig *.rej *.swp diff --git a/daemons/schedulerd/ptest.c b/daemons/schedulerd/ptest.c deleted file mode 100644 index 6f01c00673..0000000000 --- a/daemons/schedulerd/ptest.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright 2004-2018 Andrew Beekhof - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -gboolean use_stdin = FALSE; -gboolean do_simulation = FALSE; -gboolean inhibit_exit = FALSE; -gboolean all_actions = FALSE; -extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now); -extern void cleanup_calculations(pe_working_set_t * data_set); -char *use_date = NULL; - -FILE *dot_strm = NULL; - -#define DOT_PREFIX "PE_DOT: " -/* #define DOT_PREFIX "" */ - -#define dot_write(fmt...) if(dot_strm != NULL) { \ - fprintf(dot_strm, fmt); \ - fprintf(dot_strm, "\n"); \ - } else { \ - crm_debug(DOT_PREFIX""fmt); \ - } - -static void -init_dotfile(void) -{ - dot_write(" digraph \"g\" {"); -/* dot_write(" size = \"30,30\""); */ -/* dot_write(" graph ["); */ -/* dot_write(" fontsize = \"12\""); */ -/* dot_write(" fontname = \"Times-Roman\""); */ -/* dot_write(" fontcolor = \"black\""); */ -/* dot_write(" bb = \"0,0,398.922306,478.927856\""); */ -/* dot_write(" color = \"black\""); */ -/* dot_write(" ]"); */ -/* dot_write(" node ["); */ -/* dot_write(" fontsize = \"12\""); */ -/* dot_write(" fontname = \"Times-Roman\""); */ -/* dot_write(" fontcolor = \"black\""); */ -/* dot_write(" shape = \"ellipse\""); */ -/* dot_write(" color = \"black\""); */ -/* dot_write(" ]"); */ -/* dot_write(" edge ["); */ -/* dot_write(" fontsize = \"12\""); */ -/* dot_write(" fontname = \"Times-Roman\""); */ -/* dot_write(" fontcolor = \"black\""); */ -/* dot_write(" color = \"black\""); */ -/* dot_write(" ]"); */ -} - -static char * -create_action_name(action_t * action) -{ - char *action_name = NULL; - const char *action_host = NULL; - - if (action->node) { - action_host = action->node->details->uname; - action_name = crm_concat(action->uuid, action_host, ' '); - - } else if (is_set(action->flags, pe_action_pseudo)) { - action_name = strdup(action->uuid); - - } else { - action_host = ""; - action_name = crm_concat(action->uuid, action_host, ' '); - } - if (safe_str_eq(action->task, RSC_CANCEL)) { - char *tmp_action_name = action_name; - - action_name = crm_concat("Cancel", tmp_action_name, ' '); - free(tmp_action_name); - } - - return action_name; -} - -gboolean USE_LIVE_CIB = FALSE; -/* *INDENT-OFF* */ -static struct crm_option long_options[] = { - /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, - - {"simulate", 0, 0, 'S', "Simulate the transition's execution to find invalid graphs\n"}, - {"show-scores", 0, 0, 's', "Display resource allocation scores"}, - {"show-utilization", 0, 0, 'U', "Display utilization information"}, - {"all-actions", 0, 0, 'a', "Display all possible actions - even ones not part of the transition graph"}, - - {"live-check", 0, 0, 'L', "Connect to the CIB manager and use the current CIB contents as input"}, - {"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"}, - {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"}, - /* {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"}, */ - - {"save-input", 1, 0, 'I', "\tSave the input to the named file"}, - {"save-graph", 1, 0, 'G', "\tSave the transition graph (XML format) to the named file"}, - {"save-dotfile",1, 0, 'D', "Save the transition graph (DOT format) to the named file\n"}, - - {0, 0, 0, 0} -}; -/* *INDENT-ON* */ - -int -main(int argc, char **argv) -{ - GListPtr lpc = NULL; - gboolean process = TRUE; - gboolean all_good = TRUE; - enum transition_status graph_rc = -1; - crm_graph_t *transition = NULL; - crm_time_t *a_date = NULL; - cib_t *cib_conn = NULL; - - xmlNode *cib_object = NULL; - int argerr = 0; - int flag; - - char *msg_buffer = NULL; - gboolean optional = FALSE; - pe_working_set_t data_set; - - const char *source = NULL; - const char *xml_file = NULL; - const char *dot_file = NULL; - const char *graph_file = NULL; - const char *input_file = NULL; - const char *input_xml = NULL; - - /* disable glib's fancy allocators that can't be free'd */ - GMemVTable vtable; - - vtable.malloc = malloc; - vtable.realloc = realloc; - vtable.free = free; - vtable.calloc = calloc; - vtable.try_malloc = malloc; - vtable.try_realloc = realloc; - - g_mem_set_vtable(&vtable); - - crm_log_cli_init("ptest"); - crm_set_options(NULL, "[-?Vv] -[Xxp] {other options}", long_options, - "Calculate the cluster's response to the supplied cluster state\n" - "\nSuperseded by crm_simulate and likely to be removed in a future release\n\n"); - - while (1) { - int option_index = 0; - - flag = crm_get_option(argc, argv, &option_index); - if (flag == -1) - break; - - switch (flag) { - case 'S': - do_simulation = TRUE; - break; - case 'a': - all_actions = TRUE; - break; - case 'w': - inhibit_exit = TRUE; - break; - case 'X': - /*use_stdin = TRUE; */ - input_xml = optarg; - break; - case 's': - show_scores = TRUE; - break; - case 'U': - show_utilization = TRUE; - break; - case 'x': - xml_file = optarg; - break; - case 'd': - use_date = optarg; - break; - case 'D': - dot_file = optarg; - break; - case 'G': - graph_file = optarg; - break; - case 'I': - input_file = optarg; - break; - case 'V': - crm_bump_log_level(argc, argv); - break; - case 'L': - USE_LIVE_CIB = TRUE; - break; - case '$': - case '?': - crm_help(flag, CRM_EX_OK); - break; - default: - fprintf(stderr, "Option -%c is not yet supported\n", flag); - ++argerr; - break; - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) { - printf("%s ", argv[optind++]); - } - printf("\n"); - } - - if (optind > argc) { - ++argerr; - } - - if (argerr) { - crm_err("%d errors in option parsing", argerr); - crm_help('?', CRM_EX_USAGE); - } - - if (USE_LIVE_CIB) { - int rc = pcmk_ok; - - source = "live cib"; - cib_conn = cib_new(); - rc = cib_conn->cmds->signon(cib_conn, "ptest", cib_command); - - if (rc == pcmk_ok) { - crm_info("Reading XML from: live cluster"); - rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, - cib_scope_local | cib_sync_call); - - } else { - fprintf(stderr, "Live CIB query failed: %s\n", pcmk_strerror(rc)); - return 3; - } - if (cib_object == NULL) { - fprintf(stderr, "Live CIB query failed: empty result\n"); - return 3; - } - - } else if (xml_file != NULL) { - source = xml_file; - cib_object = filename2xml(xml_file); - - } else if (use_stdin) { - source = "stdin"; - cib_object = filename2xml(NULL); - } else if (input_xml) { - source = "input string"; - cib_object = string2xml(input_xml); - } - - if (cib_object == NULL && source) { - fprintf(stderr, "Could not parse configuration input from: %s\n", source); - return 4; - - } else if (cib_object == NULL) { - fprintf(stderr, "No configuration specified\n"); - crm_help('?', CRM_EX_USAGE); - } - - if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) { - create_xml_node(cib_object, XML_CIB_TAG_STATUS); - } - - if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) { - free_xml(cib_object); - return -ENOKEY; - } - - if (validate_xml(cib_object, NULL, FALSE) != TRUE) { - free_xml(cib_object); - return -pcmk_err_schema_validation; - } - - if (input_file != NULL) { - FILE *input_strm = fopen(input_file, "w"); - - if (input_strm == NULL) { - crm_perror(LOG_ERR, "Could not open %s for writing", input_file); - } else { - msg_buffer = dump_xml_formatted(cib_object); - if (fprintf(input_strm, "%s\n", msg_buffer) < 0) { - crm_perror(LOG_ERR, "Write to %s failed", input_file); - } - fflush(input_strm); - fclose(input_strm); - free(msg_buffer); - } - } - - if (use_date != NULL) { - a_date = crm_time_new(use_date); - crm_time_log(LOG_WARNING, "Set fake 'now' to", a_date, - crm_time_log_date | crm_time_log_timeofday); - crm_time_log(LOG_WARNING, "Set fake 'now' to (localtime)", a_date, - crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); - } - - set_working_set_defaults(&data_set); - if (process) { - if (show_scores && show_utilization) { - fprintf(stdout, "Allocation scores and utilization information:\n"); - } else if (show_scores) { - fprintf(stdout, "Allocation scores:\n"); - } else if (show_utilization) { - fprintf(stdout, "Utilization information:\n"); - } - do_calculations(&data_set, cib_object, a_date); - } - - msg_buffer = dump_xml_formatted(data_set.graph); - if (safe_str_eq(graph_file, "-")) { - fprintf(stdout, "%s\n", msg_buffer); - fflush(stdout); - } else if (graph_file != NULL) { - FILE *graph_strm = fopen(graph_file, "w"); - - if (graph_strm == NULL) { - crm_perror(LOG_ERR, "Could not open %s for writing", graph_file); - } else { - if (fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) { - crm_perror(LOG_ERR, "Write to %s failed", graph_file); - } - fflush(graph_strm); - fclose(graph_strm); - } - } - free(msg_buffer); - - if (dot_file != NULL) { - dot_strm = fopen(dot_file, "w"); - if (dot_strm == NULL) { - crm_perror(LOG_ERR, "Could not open %s for writing", dot_file); - } - } - - if (dot_strm == NULL) { - goto simulate; - } - - init_dotfile(); - for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) { - action_t *action = (action_t *) lpc->data; - const char *style = "filled"; - const char *font = "black"; - const char *color = "black"; - const char *fill = NULL; - char *action_name = create_action_name(action); - - crm_trace("Action %d: %p", action->id, action); - - if (is_set(action->flags, pe_action_pseudo)) { - font = "orange"; - } - - style = "dashed"; - if (is_set(action->flags, pe_action_dumped)) { - style = "bold"; - color = "green"; - - } else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { - color = "purple"; - if (all_actions == FALSE) { - goto dont_write; - } - - } else if (is_set(action->flags, pe_action_optional)) { - color = "blue"; - if (all_actions == FALSE) { - goto dont_write; - } - - } else { - color = "red"; - CRM_CHECK(is_set(action->flags, pe_action_runnable) == FALSE,; - ); - } - - set_bit(action->flags, pe_action_dumped); - dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]", - action_name, style, color, font, fill ? "fillcolor=" : "", fill ? fill : ""); - dont_write: - free(action_name); - } - - for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) { - action_t *action = (action_t *) lpc->data; - - GListPtr lpc2 = NULL; - - for (lpc2 = action->actions_before; lpc2 != NULL; lpc2 = lpc2->next) { - action_wrapper_t *before = (action_wrapper_t *) lpc2->data; - - char *before_name = NULL; - char *after_name = NULL; - const char *style = "dashed"; - - optional = TRUE; - if (before->state == pe_link_dumped) { - optional = FALSE; - style = "bold"; - } else if (is_set(action->flags, pe_action_pseudo) - && (before->type & pe_order_stonith_stop)) { - continue; - } else if (before->state == pe_link_dup) { - continue; - } else if (before->type == pe_order_none) { - continue; - } else if (is_set(before->action->flags, pe_action_dumped) - && is_set(action->flags, pe_action_dumped)) { - optional = FALSE; - } - - if (all_actions || optional == FALSE) { - before_name = create_action_name(before->action); - after_name = create_action_name(action); - dot_write("\"%s\" -> \"%s\" [ style = %s]", before_name, after_name, style); - free(before_name); - free(after_name); - } - } - } - dot_write("}"); - if (dot_strm != NULL) { - fflush(dot_strm); - fclose(dot_strm); - } - - simulate: - - if (do_simulation == FALSE) { - goto cleanup; - } - - transition = unpack_graph(data_set.graph, "ptest"); - print_graph(LOG_DEBUG, transition); - - do { - graph_rc = run_graph(transition); - - } while (graph_rc == transition_active); - - if (graph_rc != transition_complete) { - crm_crit("Transition failed: %s", transition_status(graph_rc)); - print_graph(LOG_ERR, transition); - } - destroy_graph(transition); - CRM_CHECK(graph_rc == transition_complete, all_good = FALSE; - crm_err("An invalid transition was produced")); - - cleanup: - cleanup_alloc_calculations(&data_set); - crm_log_deinit(); - - /* required for MallocDebug.app */ - if (inhibit_exit) { - GMainLoop *mainloop = g_main_loop_new(NULL, FALSE); - - g_main_loop_run(mainloop); - } - - if (all_good) { - return 0; - } - return graph_rc; -} diff --git a/doc/acls.txt b/doc/acls.txt index 35434b7f96..ce3d44f381 100644 --- a/doc/acls.txt +++ b/doc/acls.txt @@ -1,200 +1,200 @@ Pacemaker Access Control Lists ============================== Tim Serong == Introduction The various tools for administering Pacemaker clusters (crm_mon, crm shell, cibadmin and friends, Python GUI, Hawk) can be used by the +root+ user, or any user in the +haclient+ group. By default, these users have full read/write access. Starting with Pacemaker version 1.1.5, flexible access control lists are introduced to provide different levels of administration to different users. == Prerequisites * Users are regular UNIX users, so the same user accounts must be present on all nodes in the cluster. * All user accounts must be in the +haclient+ group. * Pacemaker 1.1.5 or newer must be installed on all cluster nodes. * The CIB must be configured to use the pacemaker-1.1 or 1.2 schema. This can be set by running: cibadmin --modify --xml-text '' * The +enable-acl+ option must be set. If ACLs are not explicitly enabled, the previous behaviour will be used (i.e. all users in the +haclient+ group have full access): crm configure property enable-acl=true * Once this is done, ACLs can be configured as described below. * Note that the +root+ and +hacluster+ users will always have full access. * If nonprivileged users will be using the crm shell and CLI tools (as opposed to only using Hawk or the Python GUI) they will need to have +/usr/sbin+ added to their path. == Configuring ACLs Access control lists consist of an ordered set of access rules. Each rule allows read or write access or denies access completely to a part of the CIB. Rules are typically combined to produce a specific role, then users may be assigned to that role. It is also possible to configure ACLs directly for individual users. ACLs may be configured using the crm shell or the Python GUI. The shell syntax is documented here. Using the Python GUI should be reasonably straightforward once the concepts are understood. Note that rules are applied from first to last with the first matching rule being used. This means specific +write+ rules usually need to be listed before general +read+ rules. Any permission not explicitly granted is denied by default, but note that +write+ implies +read+, so there is no need to specify both to allow full read/write access. === Minimum Required ACLs In order for the various tools to work correctly, a certain minimum amount of data must be readable by the user invoking the tool. In general, the safest thing to do is simply allow all users and roles read access to the entire CIB. === Role Syntax An ACL role is a set of rules which describe access rights to CIB. Rules consist of an access right +read+, +write+, or +deny+ and a specification denoting part of the configuration to which the access right applies. The specification can be an XPath or a combination of tag and id references. If an attribute is appended, then the specification applies only to that attribute of the matching element. ==== Usage ............... role rule [rule ...] rule :: acl-right cib-spec [attribute:] acl-right :: read | write | deny cib-spec :: xpath-spec | tag-ref-spec xpath-spec :: xpath: tag-ref-spec :: tag: | ref: | tag: ref: ............... ==== Example Role: Read-only Access ............... role monitor \ read xpath:"/cib" ............... This is a single rule which allows read-only access to the entire CIB. Users with this role will be able to view the status of the cluster, but not make any changes. ==== Example Role: ``Operator'' Access ............... role operator \ write xpath:"//crm_config//nvpair[@name='maintenance-mode']" \ write xpath:"//op_defaults//nvpair[@name='record-pending']" \ write xpath:"//nodes/node//nvpair[@name='standby']" \ write xpath:"//resources//nvpair[@name='target-role']" \ write xpath:"//resources//nvpair[@name='is-managed']" \ write xpath:"//constraints/rsc_location" \ read xpath:"/cib" ............... These rules specify that users with this role will be able to: . Turn maintenance mode on or off . Change whether pending operations are recorded . Put any node on standby, and bring any node back online . Start, stop, promote or demote any resource . Tell the cluster to manage, or not manage any resource . Migrate/move resources from node to note . View the status of the cluster Users with this role will not be able to reconfigure resources (change parameters, operations, etc.) or colocation or ordering constraints. ==== Example Role: Full Access ............... role administrator \ write xpath:"/cib" ............... This is a single rule which allows full read-write access to the entire CIB. Users with this role will have equivalent access to the +root+ and +hacluster+ users. === User Syntax ACLs can be defined for individual users using the same syntax as for roles. Alternately, users can simply be assigned a given role. The latter is considered best practice. ==== Usage ............... user {role:|rule [rule ...]} rule :: acl-right cib-spec [attribute:] acl-right :: read | write | deny cib-spec :: xpath-spec | tag-ref-spec xpath-spec :: xpath: tag-ref-spec :: tag: | ref: | tag: ref: ............... ==== Example ............... user alice role:monitor user bob role:operator ............... The above example assigns +alice+ the +monitor+ role and +bob+ the +operator+ role. == Advanced Usage Because ACLs can refer to elements and attributes in the CIB in a very granular way, it is possible to configure very specific rules. A refinement of the ``operator'' role above would be to allow manipulation of only a specific resource, for example: ............... role bigdb_admin \ write xpath:"//primitive[@id='bigdb']/meta_attributes/nvpair[@name='target-role']" \ write xpath:"//primitive[@id='bigdb']/meta_attributes/nvpair[@name='is-managed']" \ write xpath:"//constraints/rsc_location[@rsc='bigdb']" \ read ref:"bigdb" \ read xpath:"//nodes/node" uname \ read xpath:"//nodes/node" type \ read xpath:"//crm_config/cluster_property_set" \ read xpath:"/cib/status" ............... The first four rules are specific for the +bigdb+ resource. They allow modifying the +target-role+ and +is-managed+ meta attributes which effectively enables users in this role to stop/start and manage/unmanage the resource. The constraints write access rule allows moving the resource around. Finally, the user is granted read access to the resource definition. The bottom four rules are the absolute minimum read permissions necessary for proper operation of various Pacemaker tools, including `crm_mon` and the shell. This is fine for viewing cluster status, but there are some tools for which this will -not be sufficient access (notably `ptest`), which is why it is +not be sufficient access (notably `crm_simulate`), which is why it is generally recommended that read access be allowed to the entire CIB.