diff --git a/tree/20_cfe_basics/ncf_lib.cf b/tree/20_cfe_basics/ncf_lib.cf index ce32d3cb8..8bab65765 100644 --- a/tree/20_cfe_basics/ncf_lib.cf +++ b/tree/20_cfe_basics/ncf_lib.cf @@ -457,7 +457,7 @@ bundle edit_line ncf_maintain_keys_values(v, sep) { insert_lines: "${index}${sep}$i{${v}[${index}]}" comment => "Insert definition of ${index}", - ifvarclass => "!${cindex[${index}]}_key_in_file"; + ifvarclass => "!${cindex[${index}]}_ke240y_in_file"; replace_patterns: # For convergence need to use negative lookahead on value: @@ -772,3 +772,239 @@ body edit_defaults ncf_empty_select(select) max_file_size => "25M"; edit_backup => "timestamp"; } + +# defines ncf_services_${service}_${action} +# Set variables with the error +# Test behavior with unkown action +# is-active not implemented for service/init.d +bundle agent ncf_services(service, action) +{ + vars: + + ########################################################################### + # All actions + ########################################################################### + + # These commands (sadly) are not completely static (as on debian 8 the outcome depends on the service) + + # Keep in mind command execution leads to repaired status + + # systemd + pass1.!broken_systemctl.systemctl_utility_present:: + "action_command" string => "${paths.path[systemctl]} --no-ask-password ${action} ${service}.service"; + "method" string => "systemctl"; + + # svcadm/svcs + pass1.!systemctl_utility_present.svcadm_utility_present:: + # non-boot + "svc_action_cmd[start]" string => "${paths.path[svcadm]} enable -s -t ${service}"; + "svc_action_cmd[stop]" string => "${paths.path[svcadm]} disable -s -t ${service}"; + "svc_action_cmd[restart]" string => "${paths.path[svcadm]} restart -s ${service}"; + "svc_action_cmd[refresh]" string => "${paths.path[svcadm]} refresh -s ${service}"; + "svc_action_cmd[reload]" string => "${svc_action_cmd[refresh]}"; + "svc_action_cmd[is-active]" string => "${paths.path[svcs]} -H ${service} | grep '^online'"; + # boot + "svc_action_cmd[enable]" string => "${paths.path[svcadm]} enable -s ${service}"; + "svc_action_cmd[disable]" string => "${paths.path[svcadm]} disable -s ${service}"; + "svc_action_cmd[is-enabled]" string => "${paths.path[svcs]} -l ${service} | egrep '^enabled [ ]+(true|false \(temporary\))'"; + "action_command" string => "${svc_action_cmd[${action}]}"; + "method" string => "svcadm/svcs"; + + ########################################################################### + # Boot actions + ########################################################################### + + # chkconfig + pass1.is_boot_action.!systemctl_utility_present.!svcadm_utility_present.chkconfig_utility_present:: + "chkconfig_action_cmd[enable]" string => "${paths.path[chkconfig]} ${service} on"; + "chkconfig_action_cmd[disable]" string => "${paths.path[chkconfig]} ${service} off"; + "chkconfig_action_cmd[is-enabled]" string => "${paths.path[chkconfig]} --list ${service} | grep -q -e 3:on -e B:on"; + "action_command" string => "${chkconfig_action_cmd[${action}]}"; + "method" string => "chkconfig"; + + # update-rc.d + # WARN: "is-enabled" will use /etc/rcX.d/ directly + pass1.is_boot_action.!is_check_action.!systemctl_utility_present.!svcadm_utility_present.!chkconfig_utility_present.update_rcd_utility_present:: + "update_rcd_action_cmd[enable]" string => "${paths.path[update_rc_d]} ${service} defaults"; + "update_rcd_action_cmd[disable]" string => "${paths.path[update_rc_d]} ${service} disable"; + "action_command" string => "${update_rcd_action_cmd[${action}]}"; + "method" string => "update-rc.d"; + + # chitab/lsitab + pass1.is_boot_action.!systemctl_utility_present.!svcadm_utility_present.!chkconfig_utility_present.!update_rcd_utility_present.chitab_utility_present:: + "chitab_action_cmd[enable]" string => "/usr/sbin/chitab \"`lsitab -a | grep '^${service}:' | sed 's/\([^:]*\):\([^:]*\):[^:]*:\(.*\)/\1:\2:respawn:\3/'`\""; + "chitab_action_cmd[disable]" string => "/usr/sbin/chitab \"`lsitab -a | grep '^${service}:' | sed 's/\([^:]*\):\([^:]*\):[^:]*:\(.*\)/\1:\2:off:\3/'`\""; + "lsitab_action_cmd[is-enabled]" string => "/usr/sbin/lsitab -a | egrep '^${service}:[0-9]+:(respawn|boot|bootwait|wait|once|initdefault|sysinit):'"; + "action_command" string => "${lsitab_action_cmd[${action}]}"; + "method" string => "lsitab/chitab"; + + # /etc/rcX.d/ + pass1.is_boot_action.is_check_action.(broken_systemctl|(!systemctl_utility_present.!svcadm_utility_present.!chkconfig_utility_present.!chitab_utility_present)):: + "action_command" string => "${paths.path[test]} -f /etc/rc`runlevel | ${paths.path[cut]} -d' ' -f2`.d/S??${service_name}"; + "method" string => "/etc/rcX.d/"; + + ########################################################################### + # Non-boot actions + ########################################################################### + + # service + # WARN: Some actions may not be supported by the init script + pass1.!is_boot_action.!systemctl_utility_present.!svcadm_utility_present.service_utility_present:: + "action_command" string => "${paths.path[service]} ${service} ${action}"; + "method" string => "service"; + + # src + pass1.!is_boot_action.!systemctl_utility_present.!svcadm_utility_present.!service_utility_present.startsrc_utility_present:: + "svc_action_cmd[start]" string => "/usr/bin/startsrc -s ${service}"; + "svc_action_cmd[stop]" string => "/usr/bin/stopsrc -s ${service}"; + "svc_action_cmd[restart]" string => "/usr/bin/stopsrc -s ${service} && until /usr/bin/lssrc -s ${service} | ${paths.grep} -q inoperative; do ${paths.perl} -e 'select(undef,undef,undef,.25)'; done; /usr/bin/startsrc -s ${service_name}"; + "svc_action_cmd[refresh]" string => "/usr/bin/refresh -s ${service}"; + "svc_action_cmd[reload]" string => "${svc_action_cmd[refresh]}"; + "svc_action_cmd[is-active]" string => "/usr/bin/lssrc -s ${service} | grep -q 'active'"; + "action_command" string => "${svc_action_cmd[${action}]}"; + "method" string => "src"; + + # init.d + # WARN: Some actions may not be supported by the init script + pass1.!is_boot_action.!systemctl_utility_present.!svcadm_utility_present.!service_utility_present.!startsrc_utility_present.init_d_directory_present:: + "action_command" string => "/etc/init.d/${service} ${action}"; + "method" string => "/etc/init.d/"; + + ########################################################################### + + windows.is_valid_action:: + "method" string => "Windows Service Manager"; + + any:: + "canonified_service" string => canonify("${service}"); + "canonified_action" string => canonify("${action}"); + "canonified_action_command" string => canonify("${action_command}"); + + "old_class_prefix" string => "ncf_services_${canonified_service}_${canonified_action}"; + "promisers" slist => { @{this.callers_promisers}, cf_null }, policy => "ifdefined"; + "class_prefix" string => canonify(join("_", "promisers")); + "args" slist => { "${service}", "${action}" }; + + classes: + + windows:: + "is_valid_action" or => { + strcmp("start", "${action}"), + strcmp("stop", "${action}"), + strcmp("restart", "${action}"), + }; + "is_restart_action" expression => strcmp("restart", "${action}"); + + any:: + "is_boot_action" or => { + strcmp("enable", "${action}"), + strcmp("disable", "${action}"), + strcmp("is-enabled", "${action}") + }; + + "is_check_action" or => { + strcmp("is-active", "${action}"), + strcmp("is-active-process", "${action}"), + strcmp("is-enabled", "${action}") + }; + + "is_process_action" expression => strcmp("is-active-process", "${action}"); + + pass1:: + "method_found" expression => isvariable("action_command"); + + debian_8:: + # This is to workaround a bug in Debian Jessie, that makes systemctl is-enabled + # fail if run against a service that still uses SysV style scripts + # See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=760616 + "is_broken_action" expression => strcmp("is-enabled", "${action}"); + "init_script_present" expression => fileexists("/etc/init.d/${service}"); + "broken_systemctl" expression => "is_broken_action.init_script_present"; + + any:: + "pass3" expression => "pass2"; + "pass2" expression => "pass1"; + "pass1" expression => "any"; + + methods: + + pass2.!is_process_action.method_found.!systemctl_utility_present:: + "action using command" usebundle => command_execution("${action_command}"); + + "class copy" usebundle => _classes_copy("command_execution_${canonified_action_command}", "${old_class_prefix}"); + "new result classes" usebundle => _classes_copy("${class_prefix}_action_using_command", "${class_prefix}"); + + # Method not found + pass2.(!method_found.!windows)|(windows.!is_valid_action):: + "force_failure_class" usebundle => _classes_failure("${old_class_prefix}"); + "force_failure_class" usebundle => _classes_failure("${class_prefix}"); + + # Classes for process check + pass2:: + "force_failure_process" usebundle => _classes_failure("${old_class_prefix}"), + ifvarclass => "${old_class_prefix}_checked_not_ok"; + "force_failure_process" usebundle => _classes_failure("${class_prefix}"), + ifvarclass => "${old_class_prefix}_checked_not_ok"; + + "force_success_process" usebundle => _classes_success("${old_class_prefix}"), + ifvarclass => "${old_class_prefix}_checked_ok"; + "force_success_process" usebundle => _classes_success("${class_prefix}"), + ifvarclass => "${class_prefix}_checked_ok"; + + services: + + ########################################################################### + # Windows - only non-boot actions + ########################################################################### + + # Restart causes the agent to fail, so we must replace it by stop and start + windows.is_valid_action.!is_restart_action:: + "${service}" + service_policy => "${action}", + classes => classes_generic_two("${old_class_prefix}", "${class_prefix}"); + + windows.is_restart_action:: + "${service}" + service_policy => "stop", + classes => classes_generic_two("${old_class_prefix}_stop_service", "${class_prefix}_stop_service"); + + "${service}" + service_policy => "start", + classes => classes_generic_two("${old_class_prefix}", "${class_prefix}"), + ifvarclass => "(!has_promiser_stack.${old_class_prefix}_stop_service_ok)|(has_promiser_stack.${class_prefix}_stop_service_ok)"; + + processes: + + ########################################################################### + # is-active-process action + ########################################################################### + + is_process_action:: + "${service}" + process_count => any_count_two("${old_class_prefix}_checked_ok", "${class_prefix}_checked_ok"); + + "${service}" + # missing new_class_prefix : we cannot put 2 classes here, so we put the only one that we are sure works + restart_class => "${old_class_prefix}_checked_not_ok"; + + commands: + + # We need to use our own commands: implementation here rather than calling command_execution + # because of a CFEngine bug (https://dev.cfengine.com/issues/5840) that causes systemctl + # to fail if it can't find /dev/tty, which is the case unless using no_output => true + pass2.!is_process_action.systemctl_utility_present:: + "${action_command}" + contain => in_shell_and_silent, + classes => classes_generic_two("${old_class_prefix}", "${class_prefix}"); + + reports: + + pass3.error.!method_found:: + "${configuration.error} Could not find a supported service management method on this system"; + pass3.error.!is_valid_action:: + "${configuration.error} The ${action} action is not supported on Windows (use 'start', 'stop' or 'restart')"; + + pass3.info.(is_valid_action|method_found):: + "${configuration.info} Executing ${action} on ${service} using the ${method} method"; + +} diff --git a/tree/30_generic_methods/service_action.cf b/tree/30_generic_methods/service_action.cf index 18e234b4d..7f67bbcae 100644 --- a/tree/30_generic_methods/service_action.cf +++ b/tree/30_generic_methods/service_action.cf @@ -29,102 +29,23 @@ bundle agent service_action(service_name, action) { vars: - systemctl_utility_present:: - - "action_command" string => "${paths.path[systemctl]} ${action} ${service_name}.service"; - - !systemctl_utility_present.service_utility_present:: - - "action_command" string => "${paths.path[service]} ${service_name} ${action}"; - - !systemctl_utility_present.!service_utility_present.startsrc_utility_present:: - "svc_action_cmd[restart]" string => "/usr/bin/stopsrc -s ${service_name} && until /usr/bin/lssrc -s ${service_name} | ${paths.grep} -q inoperative; do ${paths.perl} -e 'select(undef,undef,undef,.25)'; done; /usr/bin/startsrc -s ${service_name}"; - "svc_action_cmd[refresh]" string => "/usr/bin/refresh -s ${service_name}"; - "svc_action_cmd[reload]" string => "${svc_action_cmd[refresh]}"; - "svc_action_cmd[start]" string => "/usr/bin/startsrc -s ${service_name}"; - "svc_action_cmd[stop]" string => "/usr/bin/stopsrc -s ${service_name}"; - "action_command" string => "${svc_action_cmd[${action}]}"; - - !systemctl_utility_present.!service_utility_present.!startsrc_utility_present.svcadm_utility_present:: - "svc_action_cmd[restart]" string => "${paths.path[svcadm]} restart -s ${service_name}"; - "svc_action_cmd[refresh]" string => "${paths.path[svcadm]} refresh -s ${service_name}"; - "svc_action_cmd[reload]" string => "${svc_action_cmd[refresh]}"; - "svc_action_cmd[enable]" string => "${paths.path[svcadm]} enable -s ${service_name}"; - "svc_action_cmd[start]" string => "${paths.path[svcadm]} enable -s -t ${service_name}"; - "svc_action_cmd[disable]" string => "${paths.path[svcadm]} disable -s ${service_name}"; - "svc_action_cmd[stop]" string => "${paths.path[svcadm]} disable -s -t ${service_name}"; - "action_command" string => "${svc_action_cmd[${action}]}"; - - !systemctl_utility_present.!service_utility_present.!startsrc_utility_present.!svcadm_utility_present.init_d_directory_present:: - - "action_command" string => "/etc/init.d/${service_name} ${action}"; - - any:: - "canonified_service_name" string => canonify("${service_name}"); - "canonified_action_command" string => canonify("${action_command}"); + "canonified_action" string => canonify("${action}"); "old_class_prefix" string => "service_action_${canonified_service_name}"; "promisers" slist => { @{this.callers_promisers}, cf_null }, policy => "ifdefined"; "class_prefix" string => canonify(join("_", "promisers")); "args" slist => { "${service_name}", "${action}" }; - classes: - - windows:: - "is_valid_action" or => { - strcmp("start", "${action}"), - strcmp("stop", "${action}"), - strcmp("restart", "${action}"), - }; - "is_restart_action" expression => strcmp("restart", "${action}"); - methods: - service_utility_present|init_d_directory_present|startsrc_utility_present|svcadm_utility_present:: - - "action using command" usebundle => command_execution("${action_command}"); - - "class copy" usebundle => _classes_copy("command_execution_${canonified_action_command}", "${old_class_prefix}"); - "new result classes" usebundle => _classes_copy("${class_prefix}_action_using_command", "${class_prefix}"); - - systemctl_utility_present|service_utility_present|init_d_directory_present|startsrc_utility_present|svcadm_utility_present|(windows.is_valid_action):: - - "report" usebundle => _log("Run action ${action} on service ${service_name}", "${old_class_prefix}", "${class_prefix}", @{args}), - ifvarclass => "(!has_promiser_stack.${old_class_prefix}_reached)|(has_promiser_stack.${class_prefix}_reached)"; - - (!systemctl_utility_present.!service_utility_present.!init_d_directory_present.!startsrc_utility_present.!svcadm_utility_present.!windows)|(windows.!is_valid_action):: - - "force_failure_class" usebundle => _classes_failure("${old_class_prefix}"); - "force_failure_class" usebundle => _classes_failure("${class_prefix}"); - "report" usebundle => _log("Running ${action} on service ${service_name} is not possible yet on this system", "${old_class_prefix}", "${class_prefix}", @{args}); - - services: - - # Restart causes the agent to fail, so we must replace it by stop and start - windows.is_valid_action.!is_restart_action:: - "${service_name}" - service_policy => "${action}", - classes => classes_generic_two("${old_class_prefix}", "${class_prefix}"); - - windows.is_restart_action:: - "${service_name}" - service_policy => "stop", - classes => classes_generic_two("${old_class_prefix}_stop_service", "${class_prefix}_stop_service"); - - "${service_name}" - service_policy => "start", - classes => classes_generic_two("${old_class_prefix}", "${class_prefix}"), - ifvarclass => "(!has_promiser_stack.${old_class_prefix}_stop_service_ok)|(has_promiser_stack.${class_prefix}_stop_service_ok)"; - - commands: + "action" usebundle => ncf_services("${service_name}", "${action}"); - # We need to use our own commands: implementation here rather than calling command_execution - # because of a CFEngine bug (https://dev.cfengine.com/issues/5840) that causes systemctl - # to fail if it can't find /dev/tty, which is the case unless using no_output => true - systemctl_utility_present:: - "${action_command}" - contain => in_shell_and_silent, - classes => classes_generic("${class_prefix}"); + "class copy" usebundle => _classes_copy("ncf_services_${canonified_service_name}_${canonified_action}", "${old_class_prefix}"), + ifvarclass => "ncf_services_${canonified_service_name}_${canonified_action}_reached"; + "new result classes" usebundle => _classes_copy("${class_prefix}_action", "${class_prefix}"), + ifvarclass => "${class_prefix}_action_reached"; + "report" usebundle => _log("Run action ${action} on service ${service_name}", "${old_class_prefix}", "${class_prefix}", @{args}), + ifvarclass => "(!has_promiser_stack.${old_class_prefix}_reached)|(has_promiser_stack.${class_prefix}_reached)"; }