Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
amousset committed Feb 28, 2017
1 parent 9b20fc2 commit 3421199
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 221 deletions.
238 changes: 238 additions & 0 deletions tree/20_cfe_basics/ncf_lib.cf
Expand Up @@ -772,3 +772,241 @@ 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
# Implementation is done in services promises
windows::
"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("method");

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.!windows.!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.!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.method_found.(!windows|is_valid_action)::
"${configuration.info} Executing ${action} on ${service} using the ${method} method";

}
95 changes: 8 additions & 87 deletions tree/30_generic_methods/service_action.cf
Expand Up @@ -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)";
}

0 comments on commit 3421199

Please sign in to comment.