bundle agent MPF_class_recommendations
{
  meta:
    (policy_server|am_policy_hub).enterprise_edition::
      "tags" slist => { "cfengine_recommends" };

  classes:
      "cfengine_recommendation_instruct_disablement"
        expression => "cfengine_recommendation_emitted",
        scope => "namespace";

  reports:
      "`cfengine_internal_purge_policies` no longer has any effect. Please use `cfengine_internal_purge_policies_disabled` instead, to choose where you want to disable purging or remove the class completely if you want purging enabled everywhere (the new default in 3.18+)." -> { "CFE-3662" }
        if => "cfengine_internal_purge_policies",
        classes => results( "bundle", "cfengine_recommendation_emitted");
}

bundle agent federated_reporting_superhub_recommendations
{
  meta:
      "tags" slist => { "cfengine_recommends" };

  classes:
      "cfengine_recommendation_instruct_disablement"
        expression => "cfengine_recommendation_emitted_reached",
        scope => "namespace";

  reports:
    cfengine_enterprise_federation:am_superhub::
      "CFEngine recommends installing gnu parallel on federated reporting superhubs."
        if => not(isgreaterthan(length(packagesmatching( "parallel", ".*", ".*", ".*")), 0 )),
        classes => results( "bundle", "cfengine_recommendation_emitted");

}
@if minimum_version(3.21.4)
# The preferred location for ignore_interfaces.rx changed from
# $(sys.inputdir)/ignore_interfaces.rx to $(sys.workdir)/ignore_interfaces.rx in
# 3.23.0 and 3.21.4. Versions lower than 3.23.0 and 3.21.4 only look for the file in inputdir.
bundle agent ignore_interfaces_rx_reccomendations
# @brief Recommend completing migration of ignore_interfaces.rx from inputdir to workdir
#
# @description This bundle identifies the presence of ignore_interfaces.rx in
# inputdir and emits messages about its presence. Additionally, the file is
# automatically copied if the file is present in inputdir and the class
# default:mpf_auto_migrate_ignore_interfaces_rx_to_workdir is defined (not
# defined by default).
{
  meta:
      "tags" slist => { "cfengine_recommends" };

  classes:
      "ignore_interfaces_in_workdir_supported"
        and => { cf_version_minimum( "3.21.4" ),
                 not( cf_version_at( "3.22" ) ) },
        comment => concat( "CFEngine doesn't look for ignore_interfaces.rx in",
                           " workdir except for versions greater than or equal",
                           "to 3.21.4." );

      "ignore_interfaces_in_workdir" -> { "ENT-9402" }
        if => fileexists( "$(sys.workdir)/ignore_interfaces.rx" );

      "ignore_interfaces_in_inputdir" -> { "ENT-9402" }
        if => fileexists( "$(sys.inputdir)/ignore_interfaces.rx" );

      "cfengine_recommendation_instruct_disablement"
        expression => "cfengine_recommendation_emitted_reached",
        scope => "namespace";

  files:
    ignore_interfaces_in_workdir_supported.default:mpf_auto_migrate_ignore_interfaces_rx_to_workdir.ignore_interfaces_in_inputdir::
      "$(sys.workdir)/ignore_interfaces.rx" -> { "ENT-9402" }
        copy_from => local_dcp( "$(sys.inputdir)/ignore_interfaces.rx"),
        comment => concat( "Excluding interfaces should be done outside of the",
                           " policy input directory so that it's easier to",
                           " ignore different interfaces on different hosts.");

    ignore_interfaces_in_workdir_supported::

      "$(sys.inputdir)/ignore_interfaces.rx" -> { "ENT-9402" }
        delete => tidy,
        action => policy( "warn" ),
        comment => concat( "Excluding interfaces should be done outside of the",
                           " policy input directory so that it's easier to",
                           " ignore different interfaces on different hosts.",
                           " This file should be deleted once it's been migrated",
                           " to sys.workdir");

  reports:

    ignore_interfaces_in_workdir_supported.(ignore_interfaces_in_inputdir.!default:mpf_auto_migrate_ignore_interfaces_rx_to_workdir_reports_disabled)::
      "NOTICE: 'ignore_interfaces.rx' is present in '$(const.dollar)(sys.inputdir)' ('$(sys.inputdir)/ignore_interfaces.rx'). We recommend that it be removed and migrated to '$(const.dollar)(sys.workdir)' ('$(sys.workdir)/ignore_interfaces.rx')"
        if => not( fileexists( "$(sys.workdir)/ignore_interfaces.rx" ) ),
        classes => results( "bundle", "cfengine_recommendation_emitted");

    ignore_interfaces_in_workdir_supported.(ignore_interfaces_in_workdir.ignore_interfaces_in_inputdir).!default:mpf_auto_migrate_ignore_interfaces_rx_to_workdir_reports_disabled::
      "NOTICE: 'ignore_interfaces.rx' identical in '$(const.dollar)(sys.workdir)' and '$(const.dollar)(sys.inputdir)'. We recommend removing '$(const.dollar)(sys.inputdir)/ignore_interfaces.rx'"
        if => strcmp( readfile( "$(sys.workdir)/ignore_interfaces.rx"),
                      readfile( "$(sys.inputdir)/ignore_interfaces.rx") ),

        classes => results( "bundle", "cfengine_recommendation_emitted");

      "NOTICE: 'ignore_interfaces.rx' in '$(const.dollar)(sys.workdir)' and '$(const.dollar)(sys.inputdir)' but not identical. We recommend verifying the desired content of '$(const.dollar)(sys.workdir)/ignore_interfaces.rx', correcting it if necessary and removing '$(const.dollar)(sys.inputdir)/ignore_interfaces.rx'"
        if => not( strcmp( readfile( "$(sys.workdir)/ignore_interfaces.rx"),
                           readfile( "$(sys.inputdir)/ignore_interfaces.rx") ) ),
        classes => results( "bundle", "cfengine_recommendation_emitted");

}
@endif

bundle agent nfs_mount_recommendations
# @brief Recommendations about configured NFS servers
{
  meta:

      "tags" slist => { "cfengine_recommends" };

  vars:
      "nfs_server_list"
        slist => getvalues( "default:inventory_linux.nfs_server"),
        depends_on => { "cfe_internal_inventory_mounted_nfs_server" };

  classes:

      # If we end up emitting the recommendation, then we define a class so that
      # instructions about disabling these reports are also emitted.

      "cfengine_recommendation_instruct_disablement"
        expression => "cfengine_recommendation_emitted_kept",
        scope => "namespace";

  reports:

      "$(with)"
        with => concat(
                        "NOTICE: At least one of your NFS servers is specified",
                        " in-consistently. Consider aligning your definitions to",
                        " consistently use or avoid a trailing dot when specifying",
                        " the NFS server."
        ),
        if => and(
                   # Check if there exists inventory of mounted nfs servers
                   isvariable( "nfs_server_list" ),
                   # Check if any other NFS server looks identical when adding a trailing dot
                   some( concat( escape("$(nfs_server_list)"), "\.$" ),
                         "nfs_server_list" )
        ),
        classes => results( "bundle", "cfengine_recommendation_emitted");
}

bundle agent postgresql_conf_recommendations
# @brief Recommendations about the configuration of postgresql.conf for CFEngine Enterprise Hubs
{
  meta:

   (policy_server|am_policy_hub).enterprise_edition::

    "tags" slist => { "cfengine_recommends" };

  vars:
    "pgsql_conf" string => "$(sys.statedir)/pg/data/postgresql.conf";
    "mem_info_source" string => "/proc/meminfo";
      "mem_info_data"
        data => data_readstringarray( $(mem_info_source), "", "(:|\s+)", inf, inf),
        if => fileexists( $(mem_info_source) );

    "upper" string => "67108864"; # 64 * 1024 * 1024 in KB
    "lower" string => "3145728"; # 3 * 1024 * 1024 in KB

      "conf[maintenance_work_mem]"
        string => "2GB",
        if => isgreaterthan( "$(mem_info_data[MemTotal][1])", $(lower) ),
        comment => "If we have more than 2GB of memory available then we set the
        maintenance_work_memory to 2G to improve index creation, and
        vacuuming. Else we leave the default value.";

      "conf[shared_buffers]"
        string => "16GB",
        if => isgreaterthan( "$(mem_info_data[MemTotal][1])", $(upper) );

      "conf[effective_cache_size]"
        string => "11GB", # 70% of 16GB
        if => isgreaterthan( "$(mem_info_data[MemTotal][1])", $(upper) );

      "calculated_shared_buffers_MB"
        string => format( "%d0",
                          eval( "$(mem_info_data[MemTotal][1]) * 25 / 100 / 1024", "math", "infix"));

      "conf[shared_buffers]"
        string => concat( $(calculated_shared_buffers_MB), "MB"),
        if => and(
                   not( isvariable( "conf[maintenance_work_mem]" ) ),
                   not( isvariable( "conf[shared_buffers]" )));

      "calculated_effective_cache_size_MB"
        string => format( "%d0",
                          eval( "$(mem_info_data[MemTotal][1]) * 70 / 100 / 1024", "math", "infix"));

      "conf[effective_cache_size]"
        string => concat( $(calculated_effective_cache_size_MB), "MB"),
        if => and(
                   not( isvariable( "conf[maintenance_work_mem]" ) ),
                   not( isvariable( "conf[effective_cache_size]" )));

  classes:
      "cfengine_recommendation_instruct_disablement"
        expression => "cfengine_recommendation_emitted_reached",
        scope => "namespace";

  files:
      "$(pgsql_conf)"
        edit_line => set_line_based("$(this.bundle).conf", "=", "\s*=\s*", ".*", "\s*#\s*"),
        classes => results( "bundle", "psql_conf_recommendations" ),
        action => policy( "warn" ),
        if => fileexists( $(pgsql_conf) );

  reports:
    psql_conf_recommendations_not_kept::
      "CFEngine Recommended Settings:";
      "You have less than $(lower)KB of total memory, please consider adding more memory."
        if => islessthan( "$(mem_info_data[MemTotal][1])", $(lower) );
      "shared_buffers = $(conf[shared_buffers])"
        if => isvariable( "conf[shared_buffers]" ),
        classes => results( "bundle", "cfengine_recommendation_emitted");
      "effective_cache_size = $(conf[effective_cache_size])"
        if => isvariable( "conf[effective_cache_size]" ),
        classes => results( "bundle", "cfengine_recommendation_emitted");
      "$(with)"
        with => concat( "maintenance_work_mem = $(conf[maintenance_work_mem]);",
                        " If we have more than 2GB of memory available then",
                        " we set the maintenance_work_memory to 2G to improve",
                        " index creation, and vacuuming. Else we leave the default value." ),
        if => isvariable( "conf[maintenance_work_mem]" ),
        classes => results( "bundle", "cfengine_recommendation_emitted");
}
