diff --git a/README.rst b/README.rst index c61eb0ccb..51179c470 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,59 @@ Install salt cloud. Install salt-ssh with roster file. Configure pillar data under salt:ssh_roster to feed the template. +``salt.standalone`` +------------------- + +Install a minion and configure it in `standalone mode +`_. + +``salt.pkgrepo`` +---------------- + +Enable the official saltstack package repository in order to always +benefit from the latest version. This state currently only works on Debian +and Ubuntu, and aims to implement the `installation recommendations of the +official documentation +`_. + +``salt.pkgrepo.absent`` +----------------------- + +Undo the effects of ``salt.pkgrepo``. + +``salt.formulas`` +----------------- + +Clone selected `Salt formulas +`_ +Git repositories under ``/srv/formulas`` and makes them available in the +relevant ``file_roots`` settings. Pillar data can be used to customize all +paths, URLs, etc. + +Here's a minimal pillar sample installing two formulas in the base +environment. + +:: + + salt_formulas: + list: + base: + - salt-formula + - openssh-formula + +See pillar.example for an exhaustive list of settings available via pillar. Note +that by default this state: + +- downloads the latest formulas from the `saltstack-formulas project + `_ on GitHub. +- does not update the local repositories after the initial clone. + This is a safety measure since you do not control how the official + repositories evolve. + +If you configure the state to download the formulas from repositories that +you control, then you can safely enable the +``salt_formulas:git_opts:default:update`` pillar setting to ``True``. + ``Configuration`` ================= Every option available in the templates can be set in pillar. Settings under 'salt' will be overridden by more specific settings under ``salt['master']``, ``salt['minion']`` or ``salt['cloud']`` diff --git a/pillar.example b/pillar.example index 1baebc6d8..5aacc2c93 100644 --- a/pillar.example +++ b/pillar.example @@ -4,13 +4,22 @@ salt: - git - roots gitfs_remotes: - - git://github.com/saltstack-formulas/salt-formula.git + - git://github.com/saltstack-formulas/salt-formula.git: - base: develop file_roots: base: - /srv/salt minion: master: salt + fileserver_backend: + - git + - roots + gitfs_remotes: + - git://github.com/saltstack-formulas/salt-formula.git: + - base: develop + file_roots: + base: + - /srv/salt module_config: test: True test.foo: foo @@ -64,3 +73,41 @@ salt_cloud_certs: -----BEGIN RSA PRIVATE KEY----- ........... -----END RSA PRIVATE KEY----- + +salt_formulas: + git_opts: + # The Git options can be customized differently for each + # environment, if an option is missing in a given environment, the + # value from "default" is used instead. + default: + # URL where the formulas git repositories are downloaded from + # it will be suffixed with .git + baseurl: https://github.com/saltstack-formulas + # Directory where Git repositories are downloaded + basedir: /srv/formulas + # Update the git repository to the latest version (False by default) + update: False + # Options passed directly to the git.latest state + options: + rev: master + dev: + basedir: /srv/formulas/dev + update: True + options: + rev: develop + # Options of the file.directory state that creates the directory where + # the git repositories of the formulas are stored + basedir_opts: + makedirs: True + user: root + group: root + mode: 755 + # List of formulas to enable in each environment + list: + base: + - salt-formula + - postfix-formula + dev: + - salt-formula + - postfix-formula + - openssh-formula diff --git a/salt/cloud.sls b/salt/cloud.sls index a4d8786dc..41c753bfe 100644 --- a/salt/cloud.sls +++ b/salt/cloud.sls @@ -10,7 +10,7 @@ pycrypto: - require: - pkg: python-pip -{% if grains['os_family'] != 'Debian' %} +{% if grains['os_family'] not in ['Debian', 'RedHat'] %} crypto: pip.installed: - require: @@ -28,7 +28,7 @@ salt-cloud: - require: - pip: apache-libcloud - pip: pycrypto - {% if grains['os_family'] != 'Debian' %} + {% if grains['os_family'] not in ['Debian', 'RedHat'] %} - pip: crypto {% endif %} diff --git a/salt/files/cloud.maps.d/saltify.conf b/salt/files/cloud.maps.d/saltify.conf new file mode 100644 index 000000000..e7e3858cf --- /dev/null +++ b/salt/files/cloud.maps.d/saltify.conf @@ -0,0 +1,7 @@ +# This file is managed by Salt via {{ source }} +make_salty: + - someinstance: + ssh_host: somehost.somedomain + ssh_username: user + password: password + sudo: True diff --git a/salt/files/cloud.profiles.d/saltify.conf b/salt/files/cloud.profiles.d/saltify.conf new file mode 100644 index 000000000..b837f4d16 --- /dev/null +++ b/salt/files/cloud.profiles.d/saltify.conf @@ -0,0 +1,3 @@ +# This file is managed by Salt via {{ source }} +make_salty: + provider: saltify diff --git a/salt/files/cloud.providers.d/ec2.conf b/salt/files/cloud.providers.d/ec2.conf index 0aeede221..fddf6b76b 100644 --- a/salt/files/cloud.providers.d/ec2.conf +++ b/salt/files/cloud.providers.d/ec2.conf @@ -1,6 +1,5 @@ # This file managed by Salt, do not edit by hand!! -{% set salt = pillar.get('salt', {}) -%} -{% set cloud = salt.get('cloud', {}) -%} +{% set cloud = salt['pillar.get']('salt:cloud', {}) -%} ec2_ubuntu_public: minion: master: {{ cloud['master'] }} @@ -14,4 +13,4 @@ ec2_ubuntu_public: location: eu-west-1 availability_zone: eu-west-1a ssh_username: ubuntu - provider: ec2 \ No newline at end of file + provider: ec2 diff --git a/salt/files/cloud.providers.d/gce.conf b/salt/files/cloud.providers.d/gce.conf index 823bf1529..5313dfbac 100644 --- a/salt/files/cloud.providers.d/gce.conf +++ b/salt/files/cloud.providers.d/gce.conf @@ -1,6 +1,5 @@ # This file managed by Salt, do not edit by hand!! -{% set salt = pillar.get('salt', {}) -%} -{% set cloud = salt.get('cloud', {}) -%} +{% set cloud = salt['pillar.get']('salt:cloud', {}) -%} gce: project: "{{ cloud['gce_project'] }}" service_account_email_address: "{{ cloud['gce_service_account_email_address'] }}" @@ -9,4 +8,4 @@ gce: master: {{ cloud['master'] }} grains: test: True - provider: gce \ No newline at end of file + provider: gce diff --git a/salt/files/cloud.providers.d/rsos.conf b/salt/files/cloud.providers.d/rsos.conf index bfb23cc73..d3d6aa76e 100644 --- a/salt/files/cloud.providers.d/rsos.conf +++ b/salt/files/cloud.providers.d/rsos.conf @@ -1,8 +1,7 @@ # This file managed by Salt, do not edit by hand!! # Providers for openstack-based Rackspace cloud servers. -{% set salt = pillar.get('salt', {}) -%} -{% set cloud = salt.get('cloud', {}) -%} +{% set cloud = salt['pillar.get']('salt:cloud', {}) -%} {% for region in cloud.get('rsos_regions', []) %} rsos_{{ region|lower }}: diff --git a/salt/files/cloud.providers.d/saltify.conf b/salt/files/cloud.providers.d/saltify.conf new file mode 100644 index 000000000..c4d5128f8 --- /dev/null +++ b/salt/files/cloud.providers.d/saltify.conf @@ -0,0 +1,3 @@ +# This file is managed by Salt via {{ source }} +saltify: + provider: saltify diff --git a/salt/files/master.d/_defaults.conf b/salt/files/master.d/_defaults.conf index 35b3d95db..0f9e29750 100644 --- a/salt/files/master.d/_defaults.conf +++ b/salt/files/master.d/_defaults.conf @@ -1,17 +1,18 @@ # This file managed by Salt, do not edit by hand!! # Based on salt version 0.17.4 default config {% set reserved_keys = ['master', 'minion', 'cloud', 'salt_cloud_certs'] -%} -{% set salt = pillar.get('salt', {}) -%} -{% set master = salt.get('master', {}) -%} +{% set cfg_salt = pillar.get('salt', {}) -%} +{% set cfg_master = cfg_salt.get('master', {}) -%} {%- macro get_config(configname, default_value) -%} -{%- if configname in master -%} -{{ configname }}: {{ master[configname] }} -{%- elif configname in salt and configname not in reserved_keys -%} -{{ configname }}: {{ salt[configname] }} +{%- if configname in cfg_master -%} +{{ configname }}: {{ cfg_master[configname] }} +{%- elif configname in cfg_salt and configname not in reserved_keys -%} +{{ configname }}: {{ cfg_salt[configname] }} {%- else -%} #{{ configname }}: {{ default_value }} {%- endif -%} {%- endmacro -%} +{%- from 'salt/formulas.jinja' import file_roots, formulas with context -%} ##### Primary configuration settings ##### ########################################## # This configuration file is used to manage the behavior of the Salt Master @@ -21,28 +22,30 @@ # Per default, the master will automatically include all config files # from master.d/*.conf (master.d is a directory in the same directory -# as the main master config file) +# as the main master config file). {{ get_config('default_include', 'master.d/*.conf') }} -# The address of the interface to bind to +# The address of the interface to bind to: {{ get_config('interface', '0.0.0.0') }} # Whether the master should listen for IPv6 connections. If this is set to True, -# the interface option must be adjusted too (for example: "interface: '::'") +# the interface option must be adjusted, too. (For example: "interface: '::'") {{ get_config('ipv6', 'False') }} -# The tcp port used by the publisher +# The tcp port used by the publisher: {{ get_config('publish_port', '4505') }} -# The user to run the salt-master as. Salt will update all permissions to -# allow the specified user to run the master. If the modified files cause -# conflicts set verify_env to False. +# The user under which the salt master will run. Salt will update all +# permissions to allow the specified user to run the master. The exception is +# the job cache, which must be deleted if this user is changed. If the +# modified files cause conflicts, set verify_env to False. {{ get_config('user', 'root') }} # Max open files +# # Each minion connecting to the master uses AT LEAST one file descriptor, the # master subscription connection. If enough minions connect you might start -# seeing on the console(and then salt-master crashes): +# seeing on the console (and then salt-master crashes): # Too many open files (tcp_listener.cpp:335) # Aborted (core dumped) # @@ -52,42 +55,47 @@ # If you wish to set a different value than the default one, uncomment and # configure this setting. Remember that this value CANNOT be higher than the # hard limit. Raising the hard limit depends on your OS and/or distribution, -# a good way to find the limit is to search the internet for(for example): +# a good way to find the limit is to search the internet. For example: # raise max open files hard limit debian # {{ get_config('max_open_files', '100000') }} -# The number of worker threads to start, these threads are used to manage -# return calls made from minions to the master, if the master seems to be -# running slowly, increase the number of threads +# The number of worker threads to start. These threads are used to manage +# return calls made from minions to the master. If the master seems to be +# running slowly, increase the number of threads. {{ get_config('worker_threads', '5') }} # The port used by the communication interface. The ret (return) port is the -# interface used for the file server, authentication, job returnes, etc. +# interface used for the file server, authentication, job returns, etc. {{ get_config('ret_port', '4506') }} -# Specify the location of the daemon process ID file +# Specify the location of the daemon process ID file: {{ get_config('pidfile', '/var/run/salt-master.pid') }} # The root directory prepended to these options: pki_dir, cachedir, # sock_dir, log_file, autosign_file, autoreject_file, extension_modules, -# key_logfile, pidfile. +# key_logfile, pidfile: {{ get_config('root_dir', '/') }} -# Directory used to store public key data +# Directory used to store public key data: {{ get_config('pki_dir', '/etc/salt/pki/master') }} -# Directory to store job and cache data +# Directory to store job and cache data: {{ get_config('cachedir', '/var/cache/salt/master') }} -# Verify and set permissions on configuration directories at startup +# Directory for custom modules. This directory can contain subdirectories for +# each of Salt's module types such as "runners", "output", "wheel", "modules", +# "states", "returners", etc. +{{ get_config('extension_modules', '') }} + +# Verify and set permissions on configuration directories at startup: {{ get_config('verify_env', 'True') }} -# Set the number of hours to keep old job information in the job cache +# Set the number of hours to keep old job information in the job cache: {{ get_config('keep_jobs', '24') }} -# Set the default timeout for the salt command and api, the default is 5 -# seconds +# Set the default timeout for the salt command and api. The default is 5 +# seconds. {{ get_config('timeout', '5') }} # The loop_interval option controls the seconds for the master's maintenance @@ -95,49 +103,136 @@ # job cache and executes the scheduler. {{ get_config('loop_interval', '60') }} -# Set the default outputter used by the salt command. The default is "nested" +# Set the default outputter used by the salt command. The default is "nested". {{ get_config('output', 'nested') }} -# By default output is colored, to disable colored output set the color value -# to False +# Return minions that timeout when running commands like test.ping +{{ get_config('show_timeout', 'True') }} + +# By default, output is colored. To disable colored output, set the color value +# to False. {{ get_config('color', 'True') }} -# Set the directory used to hold unix sockets +# Do not strip off the colored output from nested results and state outputs +# (true by default). +{{ get_config('strip_colors', 'False') }} + +# Set the directory used to hold unix sockets: {{ get_config('sock_dir', '/var/run/salt/master') }} # The master can take a while to start up when lspci and/or dmidecode is used # to populate the grains for the master. Enable if you want to see GPU hardware # data for your master. -# {{ get_config('enable_gpu_grains', 'False') }} -# The master maintains a job cache, while this is a great addition it can be +# The master maintains a job cache. While this is a great addition, it can be # a burden on the master for larger deployments (over 5000 minions). # Disabling the job cache will make previously executed jobs unavailable to # the jobs system and is not generally recommended. -# {{ get_config('job_cache', 'True') }} # Cache minion grains and pillar data in the cachedir. {{ get_config('minion_data_cache', 'True') }} +# Store all returns in the given returner. +# Setting this option requires that any returner-specific configuration also +# be set. See various returners in salt/returners for details on required +# configuration values. (See also, event_return_queue below.) +# +{{ get_config('event_return', 'mysql') }} + +# On busy systems, enabling event_returns can cause a considerable load on +# the storage system for returners. Events can be queued on the master and +# stored in a batched fashion using a single transaction for multiple events. +# By default, events are not queued. +{{ get_config('event_return_queue', '0') }} + +# Only events returns matching tags in a whitelist +{% if 'event_return_whitelist' in cfg_master -%} +event_return_whitelist: + {%- for event_return in cfg_master['event_return_whitelist'] %} + - {{ event_return }} + {%- endfor -%} +{% elif 'event_return_whitelist' in cfg_salt -%} +event_return_whitelist: + {%- for event_return in cfg_salt['event_return_whitelist'] %} + - {{ event_return }} + {%- endfor -%} +{% else -%} +# event_return_whitelist: +# - salt/master/a_tag +# - salt/master/another_tag +{% endif %} + +# Store all event returns _except_ the tags in a blacklist +{% if 'event_return_blacklist' in cfg_master -%} +event_return_blacklist: + {%- for event_return in cfg_master['event_return_blacklist'] %} + - {{ event_return }} + {%- endfor -%} +{% elif 'event_return_blacklist' in cfg_salt -%} +event_return_blacklist: + {%- for event_return in cfg_salt['event_return_blacklist'] %} + - {{ event_return }} + {%- endfor -%} +{% else -%} +# event_return_blacklist: +# - salt/master/not_this_tag +# - salt/master/or_this_one + +{% endif %} + +# Passing very large events can cause the minion to consume large amounts of +# memory. This value tunes the maximum size of a message allowed onto the +# master event bus. The value is expressed in bytes. +{{ get_config('max_event_size', '1048576') }} + +# By default, the master AES key rotates every 24 hours. The next command +# following a key rotation will trigger a key refresh from the minion which may +# result in minions which do not respond to the first command after a key refresh. +# +# To tell the master to ping all minions immediately after an AES key refresh, set +# ping_on_rotate to True. This should mitigate the issue where a minion does not +# appear to initially respond after a key is rotated. +# +# Note that ping_on_rotate may cause high load on the master immediately after +# the key rotation event as minions reconnect. Consider this carefully if this +# salt master is managing a large number of minions. +# +# If disabled, it is recommended to handle this event by listening for the +# 'aes_key_rotate' event with the 'key' tag and acting appropriately. +{{ get_config('ping_on_rotate', 'False') }} + +# By default, the master deletes its cache of minion data when the key for that +# minion is removed. To preserve the cache after key deletion, set +# 'preserve_minion_cache' to True. +# +# WARNING: This may have security implications if compromised minions auth with +# a previous deleted minion ID. +{{ get_config('preserve_minion_cache', 'False') }} + +# If max_minions is used in large installations, the master might experience +# high-load situations because of having to check the number of connected +# minions for every authentication. This cache provides the minion-ids of +# all connected minions to all MWorker-processes and greatly improves the +# performance of max_minions. +{{ get_config('con_cache', 'False') }} + # The master can include configuration from other files. To enable this, # pass a list of paths to this option. The paths can be either relative or # absolute; if relative, they are considered to be relative to the directory # the main master configuration file lives in (this file). Paths can make use # of shell-style globbing. If no files are matched by a path passed to this -# option then the master will log a warning message. -# +# option, then the master will log a warning message. # # Include a config file from some other path: -#include: /etc/salt/extra_config +# include: /etc/salt/extra_config # # Include config from several files and directories: -#include: -# - /etc/salt/extra_config +# include: +# - /etc/salt/extra_config {{ get_config('include', '[]') }} - ##### Security settings ##### ########################################## # Enable "open mode", this mode still maintains encryption, but turns off @@ -150,6 +245,12 @@ # public keys from the minions. Note that this is insecure. {{ get_config('auto_accept', 'False') }} +# Time in minutes that a incoming public key with a matching name found in +# pki_dir/minion_autosign/keyid is automatically accepted. Expired autosign keys +# are removed when the master checks the minion_autosign directory. +# 0 equals no timeout +{{ get_config('autosign_timeout', '120') }} + # If the autosign_file is specified, incoming keys specified in the # autosign_file will be automatically accepted. This is insecure. Regular # expressions as well as globing lines are supported. @@ -158,72 +259,93 @@ # Works like autosign_file, but instead allows you to specify minion IDs for # which keys will automatically be rejected. Will override both membership in # the autosign_file and the auto_accept setting. -{{ get_config('autoreject_file', '/etc/salt/autosign.conf') }} +{{ get_config('autoreject_file', '/etc/salt/autoreject.conf') }} -# Enable permissive access to the salt keys. This allows you to run the +# Enable permissive access to the salt keys. This allows you to run the # master or minion as root, but have a non-root group be given access to -# your pki_dir. To make the access explicit, root must belong to the group -# you've given access to. This is potentially quite insecure. -# If an autosign_file is specified, enabling permissive_pki_access will allow group access -# to that specific file. +# your pki_dir. To make the access explicit, root must belong to the group +# you've given access to. This is potentially quite insecure. If an autosign_file +# is specified, enabling permissive_pki_access will allow group access to that +# specific file. {{ get_config('permissive_pki_access', 'False') }} # Allow users on the master access to execute specific commands on minions. # This setting should be treated with care since it opens up execution # capabilities to non root users. By default this capability is completely # disabled. -# #client_acl: # larry: # - test.ping # - network.* -# -{{ get_config('client_acl', '{}') }} +{% if 'client_acl' in cfg_master -%} +client_acl: +{%- for name, user in cfg_master['client_acl']|dictsort %} + {{ name}}: +{%- for command in user %} + - {% raw %}'{% endraw %}{{ command }}{% raw %}'{% endraw %} +{%- endfor -%} +{%- endfor -%} +{% elif 'client_acl' in cfg_salt -%} +client_acl: +{%- for name, user in cfg_salt['client_acl']|dictsort %} + {{ name }}: +{%- for command in user %} + - {% raw %}'{% endraw %}{{ command }}{% raw %}'{% endraw %} +{%- endfor -%} +{%- endfor -%} +{% else -%} +#client_acl: +# larry: +# - test.ping +# - network.* +{%- endif %} # Blacklist any of the following users or modules # # This example would blacklist all non sudo users, including root from # running any commands. It would also blacklist any use of the "cmd" -# module. -# This is completely disabled by default. +# module. This is completely disabled by default. # -{% if 'client_acl_blacklist' in master -%} +{% if 'client_acl_blacklist' in cfg_master %} client_acl_blacklist: users: - {% for user in master['client_acl_blacklist'].get('users', []) -%} + {% for user in cfg_master['client_acl_blacklist'].get('users', []) %} - {{ user }} - {% endfor -%} + {% endfor %} modules: - {% for mod in master['client_acl_blacklist'].get('modules', []) -%} + {% for mod in cfg_master['client_acl_blacklist'].get('modules', []) %} - {{ mod }} - {% endfor -%} -{% elif 'client_acl_blacklist' in salt -%} + {% endfor %} +{% elif 'client_acl_blacklist' in cfg_salt %} client_acl_blacklist: users: - {% for user in salt['client_acl_blacklist'].get('users', []) -%} + {% for user in cfg_salt['client_acl_blacklist'].get('users', []) %} - {{ user }} - {% endfor -%} + {% endfor %} modules: - {% for mod in salt['client_acl_blacklist'].get('modules', []) -%} + {% for mod in cfg_salt['client_acl_blacklist'].get('modules', []) %} - {{ mod }} - {% endfor -%} -{% else -%} + {% endfor %} +{% else %} #client_acl_blacklist: # users: # - root # - '^(?!sudo_).*$' # all non sudo users # modules: # - cmd -{%- endif %} +{% endif %} + +# Enforce client_acl & client_acl_blacklist when users have sudo +# access to the salt command. +# +{{ get_config('sudo_acl', 'False') }} # The external auth system uses the Salt auth modules to authenticate and # validate users to access areas of the Salt system. -# #external_auth: # pam: # fred: # - test.* -# {{ get_config('external_auth', '{}') }} # Time (in seconds) for a newly generated token to live. Default: 12 hours @@ -234,8 +356,7 @@ client_acl_blacklist: {{ get_config('file_recv', 'False') }} # Set a hard-limit on the size of the files that can be pushed to the master. -# It will be interpreted as megabytes. -# Default: 100 +# It will be interpreted as megabytes. Default: 100 {{ get_config('file_recv_max_size', '100') }} # Signature verification on messages published from the master. @@ -249,20 +370,19 @@ client_acl_blacklist: # no signature, it will still be accepted, and a warning message will be logged. # Conversely, if sign_pub_messages is False, but a minion receives a signed # message it will be accepted, the signature will not be checked, and a warning message -# will be logged. This behavior will go away in Salt 0.17.6 (or Hydrogen RC1, whichever -# comes first) and these two situations will cause minion to throw an exception and -# drop the message. -# +# will be logged. This behavior went away in Salt 2014.1.0 and these two situations +# will cause minion to throw an exception and drop the message. {{ get_config('sign_pub_message', 'False') }} + ##### Master Module Management ##### ########################################## -# Manage how master side modules are loaded +# Manage how master side modules are loaded. -# Add any additional locations to look for master runners +# Add any additional locations to look for master runners: {{ get_config('runner_dirs', '[]') }} -# Enable Cython for master side modules +# Enable Cython for master side modules: {{ get_config('cython_enable', 'False') }} @@ -276,6 +396,7 @@ client_acl_blacklist: # The master_tops option replaces the external_nodes option by creating # a plugable system for the generation of external top data. The external_nodes # option is deprecated by the master_tops option. +# # To gain the capabilities of the classic external_nodes system, use the # following configuration: # master_tops: @@ -319,10 +440,17 @@ client_acl_blacklist: # output for each changed state if set to 'full', but if set to 'terse' # the output will be shortened to a single line. If set to 'mixed', the output # will be terse unless a state failed, in which case that output will be full. +# If set to 'changes', the output will be full unless the state didn't change. {{ get_config('state_output', 'full') }} -{{ get_config('yaml_utf8', 'False') }} - +# Automatically aggregate all states that have support for mod_aggregate by +# setting to True. Or pass a list of state module names to automatically +# aggregate just those types. +# +# state_aggregate: +# - pkg +# +#state_aggregate: False ##### File Server settings ##### ########################################## @@ -344,23 +472,14 @@ client_acl_blacklist: # prod: # - /srv/salt/prod/services # - /srv/salt/prod/states -{% if 'file_roots' in master -%} -file_roots: -{%- for name, roots in master['file_roots']|dictsort %} - {{ name }}: -{%- for dir in roots %} - - {{ dir }} -{%- endfor -%} -{%- endfor -%} -{% elif 'file_roots' in salt -%} -file_roots: -{%- for name, roots in salt['file_roots']|dictsort %} - {{ name }}: -{%- for dir in roots %} - - {{ dir }} -{%- endfor -%} -{%- endfor -%} -{% else -%} + +{% if 'file_roots' in cfg_master -%} +{{ file_roots(cfg_master['file_roots']) }} +{%- elif 'file_roots' in cfg_salt -%} +{{ file_roots(cfg_salt['file_roots']) }} +{%- elif formulas|length -%} +{{ file_roots({'base': ['/srv/salt']}) }} +{%- else -%} #file_roots: # base: # - /srv/salt @@ -369,6 +488,9 @@ file_roots: # The hash_type is the hash to use when discovering the hash of a file on # the master server. The default is md5, but sha1, sha224, sha256, sha384 # and sha512 are also supported. +# +# Prior to changing this value, the master should be stopped and all Salt +# caches should be cleared. {{ get_config('hash_type', 'md5') }} # The buffer size in the file server can be adjusted here: @@ -380,95 +502,90 @@ file_roots: # For example, if you manage your custom modules and states in subversion # and don't want all the '.svn' folders and content synced to your minions, # you could set this to '/\.svn($|/)'. By default nothing is ignored. -{% if 'file_ignore_regex' in master -%} +{% if 'file_ignore_regex' in cfg_master %} file_ignore_regex: - {% for regex in master['file_ignore_regex'] -%} + {% for regex in cfg_master['file_ignore_regex'] %} - {{ regex }} - {% endfor -%} -{% elif 'file_ignore_regex' in salt -%} + {% endfor %} +{% elif 'file_ignore_regex' in cfg_salt %} file_ignore_regex: - {% for regex in salt['file_ignore_regex'] -%} + {% for regex in cfg_salt['file_ignore_regex'] %} - {{ regex }} - {% endfor -%} -{% else -%} + {% endfor %} +{% else %} #file_ignore_regex: # - '/\.svn($|/)' # - '/\.git($|/)' -{%- endif %} +{% endif %} # A file glob (or list of file globs) that will be matched against the file # path before syncing the modules and states to the minions. This is similar # to file_ignore_regex above, but works on globs instead of regex. By default # nothing is ignored. -{% if 'file_ignore_glob' in master -%} +{% if 'file_ignore_glob' in cfg_master %} file_ignore_glob: - {% for glob in master['file_ignore_glob'] -%} + {% for glob in cfg_master['file_ignore_glob'] %} - {{ glob }} - {% endfor -%} -{% elif 'file_ignore_glob' in salt -%} + {% endfor %} +{% elif 'file_ignore_glob' in cfg_salt %} file_ignore_glob: - {% for glob in salt['file_ignore_glob'] -%} + {% for glob in cfg_salt['file_ignore_glob'] %} - {{ glob }} - {% endfor -%} -{% else -%} + {% endfor %} +{% else %} # file_ignore_glob: # - '*.pyc' # - '*/somefolder/*.bak' # - '*.swp' -{%- endif %} +{% endif %} # File Server Backend +# # Salt supports a modular fileserver backend system, this system allows # the salt master to link directly to third party systems to gather and # manage the files available to minions. Multiple backends can be # configured and will be searched for the requested file in the order in which # they are defined here. The default setting only enables the standard backend # "roots" which uses the "file_roots" option. -# #fileserver_backend: # - roots # # To use multiple backends list them in the order they are searched: -# #fileserver_backend: # - git # - roots -{% if 'fileserver_backend' in master -%} +{% if 'fileserver_backend' in cfg_master -%} fileserver_backend: -{%- for backend in master['fileserver_backend'] %} +{%- for backend in cfg_master['fileserver_backend'] %} - {{ backend }} -{% endfor -%} -{% endif %} +{%- endfor -%} +{%- endif %} # # Uncomment the line below if you do not want the file_server to follow # symlinks when walking the filesystem tree. This is set to True # by default. Currently this only applies to the default roots # fileserver_backend. -# {{ get_config('fileserver_followsymlinks', 'False') }} # # Uncomment the line below if you do not want symlinks to be # treated as the files they are pointing to. By default this is set to # False. By uncommenting the line below, any detected symlink while listing # files on the Master will not be returned to the Minion. -# {{ get_config('fileserver_ignoresymlinks', 'True') }} - # # By default, the Salt fileserver recurses fully into all defined environments # to attempt to find files. To limit this behavior so that the fileserver only # traverses directories with SLS files and special Salt directories like _modules, # enable the option below. This might be useful for installations where a file root # has a very large number of files and performance is impacted. Default is False. -# {{ get_config('fileserver_limit_traversal', 'False') }} # # The fileserver can fire events off every time the fileserver is updated, # these are disabled by default, but can be easily turned on by setting this # flag to True {{ get_config('fileserver_events', 'False') }} -# -# Git fileserver backend configuration + +# Git File Server Backend Configuration # # Gitfs can be provided by one of two python modules: GitPython or pygit2. If # using pygit2, both libgit2 and git must also be installed. @@ -483,24 +600,24 @@ fileserver_backend: # environments. # Note: file:// repos will be treated as a remote, so refs you want used must # exist in that repo as *local* refs. -{% if 'gitfs_remotes' in master -%} +{% if 'gitfs_remotes' in cfg_master -%} gitfs_remotes: -{%- for remote in master['gitfs_remotes'] %} -{% if remote is iterable %} - {%- for repo, children in remote.iteritems() -%} +{%- for remote in cfg_master['gitfs_remotes'] %} +{%- if remote is iterable and remote is not string %} + {%- for repo, children in remote.items() %} - {{ repo }}: {%- for child in children %} - {% for key, value in child.iteritems() -%} + {%- for key, value in child.items() %} - {{ key }}: {{ value }} - {%- endfor %} - {%- endfor %} - {%- endfor %} - {%- else -%} + {%- endfor -%} + {%- endfor -%} + {%- endfor -%} + {%- else %} - {{ remote }} +{%- endif -%} +{%- endfor -%} {%- endif %} -{%- endfor %} -{%- endif %} -# + #gitfs_remotes: # - git://github.com/saltstack/salt-states.git # - file:///var/git/saltmaster @@ -512,7 +629,6 @@ gitfs_remotes: # is a security concern, you may want to try using the ssh transport. {{ get_config('gitfs_ssl_verify', 'True') }} # -# # The gitfs_root option gives the ability to serve files from a subdirectory # within the repository. The path is defined relative to the root of the # repository and defaults to the repository root. @@ -526,43 +642,48 @@ gitfs_remotes: # Pillar is laid out in the same fashion as the file server, with environments, # a top file and sls files. However, pillar data does not need to be in the # highstate format, and is generally just key/value pairs. -{% if 'pillar_roots' in master -%} +{% if 'pillar_roots' in cfg_master %} pillar_roots: -{%- for name, roots in master['pillar_roots']|dictsort %} +{% for name, roots in cfg_master['pillar_roots']|dictsort %} {{ name }}: -{%- for dir in roots %} +{% for dir in roots %} - {{ dir }} -{%- endfor -%} -{%- endfor -%} -{% elif 'pillar_roots' in salt -%} +{% endfor %} +{% endfor %} +{% elif 'pillar_roots' in cfg_salt %} pillar_roots: -{%- for name, roots in salt['pillar_roots']|dictsort %} +{% for name, roots in cfg_salt['pillar_roots']|dictsort %} {{ name }}: -{%- for dir in roots %} +{% for dir in roots %} - {{ dir }} -{%- endfor -%} -{%- endfor -%} +{% endfor %} +{% endfor %} {% else %} #pillar_roots: # base: # - /srv/pillar -{%- endif %} - -{% if 'ext_pillar' in master -%} +{% endif %} +# +{% if 'ext_pillar' in cfg_master %} ext_pillar: -{% for pillar in master['ext_pillar'] %} +{% for pillar in cfg_master['ext_pillar'] %} - {{ pillar.items()[0][0] }}: {{ pillar.items()[0][1] }} -{% endfor -%} -{% elif 'ext_pillar' in salt -%} +{% endfor %} +{% elif 'ext_pillar' in cfg_salt %} ext_pillar: -{% for pillar in salt['ext_pillar'] %} +{% for pillar in cfg_salt['ext_pillar'] %} - {{ pillar.items()[0][0] }}: {{ pillar.items()[0][1] }} -{% endfor -%} +{% endfor %} {% else %} #ext_pillar: # - hiera: /etc/hiera.yaml # - cmd_yaml: cat /etc/salt/yaml -{%- endif %} +{% endif %} + +# The ext_pillar_first option allows for external pillar sources to populate +# before file system pillar. This allows for targeting file system pillar from +# ext_pillar. +{{ get_config('ext_pillar_first', 'False') }} # The pillar_gitfs_ssl_verify option specifies whether to ignore ssl certificate # errors when contacting the pillar gitfs backend. You might want to set this to @@ -594,13 +715,13 @@ ext_pillar: # this master where to receive commands from. {{ get_config('syndic_master', 'masterofmaster') }} -# This is the 'ret_port' of the MasterOfMaster +# This is the 'ret_port' of the MasterOfMaster: {{ get_config('syndic_master_port', '4506') }} -# PID file of the syndic daemon +# PID file of the syndic daemon: {{ get_config('syndic_pidfile', '/var/run/salt-syndic.pid') }} -# LOG file of the syndic daemon +# LOG file of the syndic daemon: {{ get_config('syndic_log_file', 'syndic.log') }} ##### Peer Publish settings ##### @@ -614,37 +735,35 @@ ext_pillar: # of regular expressions to match functions. The following will allow the # minion authenticated as foo.example.com to execute functions from the test # and pkg modules. -# #peer: # foo.example.com: # - test.* # - pkg.* # # This will allow all minions to execute all commands: -# #peer: # .*: # - .* # # This is not recommended, since it would allow anyone who gets root on any # single minion to instantly have root on all of the minions! -{% if 'peer' in master -%} +{% if 'peer' in cfg_master %} peer: - {% for name, roots in master['peer'].items() -%} + {% for name, roots in cfg_master['peer'].items() %} {{ name }}: - {% for mod in roots -%} + {% for mod in roots %} - {{ mod }} - {% endfor -%} - {% endfor -%} -{% elif 'peer' in salt -%} + {% endfor %} + {% endfor %} +{% elif 'peer' in cfg_salt %} peer: - {% for name, roots in salt['peer'].items() -%} + {% for name, roots in cfg_salt['peer'].items() %} {{ name }}: - {% for mod in roots -%} + {% for mod in roots %} - {{ mod }} - {% endfor -%} - {% endfor -%} -{% endif -%} + {% endfor %} + {% endfor %} +{% endif %} # Minions can also be allowed to execute runners from the salt master. # Since executing a runner from the minion could be considered a security risk, @@ -653,55 +772,53 @@ peer: # # All peer runner support is turned off by default and must be enabled before # using. This will enable all peer runners for all minions: -# #peer_run: # .*: # - .* # # To enable just the manage.up runner for the minion foo.example.com: -# #peer_run: # foo.example.com: # - manage.up -{% if 'peer_run' in master -%} +{% if 'peer_run' in cfg_master %} peer_run: - {% for name, roots in master['peer_run'].items() -%} + {% for name, roots in cfg_master['peer_run'].items() %} {{ name }}: - {% for mod in roots -%} + {% for mod in roots %} - {{ mod }} - {% endfor -%} - {% endfor -%} -{% elif 'peer_run' in salt -%} + {% endfor %} + {% endfor %} +{% elif 'peer_run' in cfg_salt %} peer_run: - {% for name, roots in salt['peer_run'].items() -%} + {% for name, roots in cfg_salt['peer_run'].items() %} {{ name }}: - {% for mod in roots -%} + {% for mod in roots %} - {{ mod }} - {% endfor -%} - {% endfor -%} -{% endif -%} + {% endfor %} + {% endfor %} +{% endif %} + ##### Mine settings ##### ########################################## # Restrict mine.get access from minions. By default any minion has a full access # to get all mine data from master cache. In acl definion below, only pcre matches # are allowed. -# # mine_get: # .*: # - .* # -# Example below enables minion foo.example.com to get 'network.interfaces' mine data only -# , minions web* to get all network.* and disk.* mine data and all other minions won't get -# any mine data. -# +# The example below enables minion foo.example.com to get 'network.interfaces' mine +# data only, minions web* to get all network.* and disk.* mine data and all other +# minions won't get any mine data. # mine_get: # foo.example.com: -# - network.inetrfaces +# - network.interfaces # web.*: # - network.* # - disk.* + ##### Logging settings ##### ########################################## # The location of the master log file @@ -738,19 +855,19 @@ peer_run: # example sets the main salt library at the 'warning' level, but sets # 'salt.modules' to log at the 'debug' level: # log_granular_levels: -# 'salt': 'warning', +# 'salt': 'warning' # 'salt.modules': 'debug' # -{% if 'log_granular_levels' in master %} +{% if 'log_granular_levels' in cfg_master %} log_granular_levels: - {% for name, lvl in master['log_granular_levels'] %} + {% for name, lvl in cfg_master['log_granular_levels'].items() %} {{ name }}: {{ lvl }} - {% endfor -%} -{% elif 'log_granular_levels' in salt %} + {% endfor %} +{% elif 'log_granular_levels' in cfg_salt %} log_granular_levels: - {% for name, lvl in salt['log_granular_levels'] %} + {% for name, lvl in cfg_salt['log_granular_levels'].items() %} {{ name }}: {{ lvl }} - {% endfor -%} + {% endfor %} {% else %} #log_granular_levels: {} {% endif %} @@ -758,75 +875,80 @@ log_granular_levels: ##### Node Groups ##### ########################################## -# Node groups allow for logical groupings of minion nodes. -# A group consists of a group name and a compound target. -# +# Node groups allow for logical groupings of minion nodes. A group consists of a group +# name and a compound target. #nodegroups: # group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com' # group2: 'G@os:Debian and foo.domain.com' -{% if 'nodegroups' in master %} +{% if 'nodegroups' in cfg_master %} nodegroups: - {% for name, lvl in master['nodegroups'] %} + {% for name, lvl in cfg_master['nodegroups'].items() %} {{ name }}: {{ lvl }} - {% endfor -%} -{% elif 'nodegroups' in salt %} + {% endfor %} +{% elif 'nodegroups' in cfg_salt %} nodegroups: - {% for name, lvl in salt['nodegroups'] %} + {% for name, lvl in cfg_salt['nodegroups'].items() %} {{ name }}: {{ lvl }} - {% endfor -%} + {% endfor %} {% endif %} ##### Range Cluster settings ##### ########################################## # The range server (and optional port) that serves your cluster information -# https://github.com/grierj/range/wiki/Introduction-to-Range-with-YAML-files +# https://github.com/ytoolshed/range/wiki/%22yamlfile%22-module-file-spec # {{ get_config('range_server', 'range:80') }} - ##### Windows Software Repo settings ##### ############################################## -# Location of the repo on the master +# Location of the repo on the master: {{ get_config('win_repo', '/srv/salt/win/repo') }} -# Location of the master's repo cache file +# Location of the master's repo cache file: {{ get_config('win_repo_mastercachefile', '/srv/salt/win/repo/winrepo.p') }} -# List of git repositories to include with the local repo -{% if 'win_gitrepos' in master %} +# List of git repositories to include with the local repo: +{% if 'win_gitrepos' in cfg_master %} win_gitrepos: - {% for repo in master['win_gitrepos'] %} + {% for repo in cfg_master['win_gitrepos'] %} - {{ repo }} - {% endfor -%} -{% elif 'win_gitrepos' in salt %} + {% endfor %} +{% elif 'win_gitrepos' in cfg_salt %} win_gitrepos: - {% for repo in salt['win_gitrepos'] %} + {% for repo in cfg_salt['win_gitrepos'] %} - {{ repo }} - {% endfor -%} + {% endfor %} {% else %} #win_gitrepos: # - 'https://github.com/saltstack/salt-winrepo.git' {% endif %} -{% if 'halite' in master -%} +##### Returner settings ###### +############################################ +# Which returner(s) will be used for minion's result: +#return: mysql + +{% if 'halite' in cfg_master %} ##### Halite ##### ########################################## halite: - {%- for name, value in master['halite'].iteritems() %} + {% for name, value in cfg_master['halite'].items() %} {{ name }}: {{ value }} - {%- endfor %} -{%- endif %} + {% endfor %} +{% endif %} -{% if 'rest_cherrypy' in master -%} +{% if 'rest_cherrypy' in cfg_master %} ##### rest_cherrypy ##### ########################################## rest_cherrypy: - {%- for name, value in master['rest_cherrypy'].iteritems() %} + {% for name, value in cfg_master['rest_cherrypy'].items() %} {{ name }}: {{ value }} {%- endfor %} {%- endif %} +{% if 'presence_events' in cfg_master %} ##### presence events ##### ########################################## {{ get_config('presence_events', 'False') }} +{% endif %} diff --git a/salt/files/minion.d/_defaults.conf b/salt/files/minion.d/_defaults.conf index 2332df661..2f70112c0 100644 --- a/salt/files/minion.d/_defaults.conf +++ b/salt/files/minion.d/_defaults.conf @@ -1,17 +1,18 @@ # This file managed by Salt, do not edit by hand!! # Based on salt version 0.17.4 default config {% set reserved_keys = ['master', 'minion', 'cloud', 'salt_cloud_certs'] -%} -{% set salt = pillar.get('salt', {}) -%} -{% set minion = salt.get('minion', {}) -%} +{% set cfg_salt = pillar.get('salt', {}) -%} +{% set cfg_minion = cfg_salt.get('minion', {}) -%} {%- macro get_config(configname, default_value) -%} -{%- if configname in minion -%} -{{ configname }}: {{ minion[configname] }} -{%- elif configname in salt and configname not in reserved_keys -%} -{{ configname }}: {{ salt[configname] }} +{%- if configname in cfg_minion -%} +{{ configname }}: {{ cfg_minion[configname] }} +{%- elif configname in cfg_salt and configname not in reserved_keys -%} +{{ configname }}: {{ cfg_salt[configname] }} {%- else -%} #{{ configname }}: {{ default_value }} {%- endif -%} {%- endmacro -%} +{%- from 'salt/formulas.jinja' import file_roots, formulas with context -%} ##### Primary configuration settings ##### ########################################## @@ -20,18 +21,18 @@ # as the main minion config file). {{ get_config('default_include', 'minion.d/*.conf') }} -# Set the location of the salt master server, if the master server cannot be +# Set the location of the salt master server. If the master server cannot be # resolved, then the minion will fail to start. {{ get_config('master', 'salt') }} # If multiple masters are specified in the 'master' setting, the default behavior # is to always try to connect to them in the order they are listed. If random_master is # set to True, the order will be randomized instead. This can be helpful in distributing -# the load of many minions executing salt-call requests, for example from a cron job. +# the load of many minions executing salt-call requests, for example, from a cron job. # If only one master is listed, this setting is ignored and a warning will be logged. {{ get_config('random_master', 'False') }} -# Set whether the minion should connect to the master via IPv6 +# Set whether the minion should connect to the master via IPv6: {{ get_config('ipv6', 'False') }} # Set the number of seconds to wait before attempting to resolve @@ -39,13 +40,13 @@ # Set to zero if the minion should shutdown and not retry. {{ get_config('retry_dns', '30') }} -# Set the port used by the master reply and authentication server +# Set the port used by the master reply and authentication server. {{ get_config('master_port', '4506') }} -# The user to run salt +# The user to run salt. {{ get_config('user', 'root') }} -# Specify the location of the daemon process ID file +# Specify the location of the daemon process ID file. {{ get_config('pidfile', '/var/run/salt-minion.pid') }} # The root directory prepended to these options: pki_dir, cachedir, log_file, @@ -60,8 +61,8 @@ # Since salt uses detached ids it is possible to run multiple minions on the # same machine but with different ids, this can be useful for salt compute # clusters. -{% if 'id' in minion -%} -id: {{ minion['id'] }} +{% if 'id' in cfg_minion -%} +id: {{ cfg_minion['id'] }} {% else -%} #id: {%- endif %} @@ -73,7 +74,7 @@ id: {{ minion['id'] }} # Custom static grains for this minion can be specified here and used in SLS # files just like all other grains. This example sets 4 custom grains, with -# the 'roles' grain having two values that can be matched against: +# the 'roles' grain having two values that can be matched against. #grains: # roles: # - webserver @@ -83,35 +84,38 @@ id: {{ minion['id'] }} # cab_u: 14-15 {{ get_config('grains', '{}') }} -# Where cache data goes +# Where cache data goes. {{ get_config('cachedir', '/var/cache/salt/minion') }} -# Verify and set permissions on configuration directories at startup +# Verify and set permissions on configuration directories at startup. {{ get_config('verify_env', 'True') }} # The minion can locally cache the return data from jobs sent to it, this # can be a good way to keep track of jobs the minion has executed -# (on the minion side). By default this feature is disabled, to enable -# set cache_jobs to True +# (on the minion side). By default this feature is disabled, to enable, set +# cache_jobs to True. {{ get_config('cache_jobs', 'False') }} -# set the directory used to hold unix sockets +# Set the directory used to hold unix sockets. {{ get_config('sock_dir', '/var/run/salt/minion') }} # Set the default outputter used by the salt-call command. The default is -# "nested" +# "nested". {{ get_config('output', 'nested') }} # -# By default output is colored, to disable colored output set the color value -# to False +# By default output is colored. To disable colored output, set the color value +# to False. {{ get_config('color', 'True') }} +# Do not strip off the colored output from nested results and state outputs +# (true by default). +{{ get_config('strip_colors', 'False') }} + # Backup files that are replaced by file.managed and file.recurse under # 'cachedir'/file_backups relative to their original location and appended # with a timestamp. The only valid setting is "minion". Disabled by default. # # Alternatively this can be specified for each file in state files: -# # /etc/ssh/sshd_config: # file.managed: # - source: salt://ssh/sshd_config @@ -129,23 +133,46 @@ id: {{ minion['id'] }} # set to zero, the time between reconnection attempts will stay constant. {{ get_config('acceptance_wait_time_max', '0') }} +# If the master rejects the minion's public key, retry instead of exiting. +# Rejected keys will be handled the same as waiting on acceptance. +{{ get_config('rejected_retry', 'False') }} + # When the master key changes, the minion will try to re-auth itself to receive # the new master key. In larger environments this can cause a SYN flood on the # master because all minions try to re-auth immediately. To prevent this and # have a minion wait for a random amount of time, use this optional parameter. -# The wait-time will be a random number of seconds between -# 0 and the defined value. +# The wait-time will be a random number of seconds between 0 and the defined value. {{ get_config('random_reauth_delay', '60') }} # When waiting for a master to accept the minion's public key, salt will # continuously attempt to reconnect until successful. This is the timeout value, # in seconds, for each individual attempt. After this timeout expires, the minion -# will wait for acceptance_wait_time seconds before trying again. -# Unless your master is under unusually heavy load, this should be left at the default. -{{ get_config('auth_timeout', '3') }} +# will wait for acceptance_wait_time seconds before trying again. Unless your master +# is under unusually heavy load, this should be left at the default. +{{ get_config('auth_timeout', '60') }} + +# Number of consecutive SaltReqTimeoutError that are acceptable when trying to +# authenticate. +{{ get_config('auth_tries', '7') }} + +# If authentication fails due to SaltReqTimeoutError during a ping_interval, +# cause sub minion process to restart. +{{ get_config('auth_safemode', 'False') }} +# Ping Master to ensure connection is alive (minutes). +{{ get_config('ping_interval', '0') }} -# If you don't have any problems with syn-floods, dont bother with the +# To auto recover minions if master changes IP address (DDNS) +# auth_tries: 10 +# auth_safemode: False +# ping_interval: 90 +# restart_on_error: True +# +# Minions won't know master is missing until a ping fails. After the ping fail, +# the minion will attempt authentication and likely fails out and cause a restart. +# When the minion restarts it will resolve the masters IP and attempt to reconnect. + +# If you don't have any problems with syn-floods, don't bother with the # three recon_* settings described below, just leave the defaults! # # The ZeroMQ pull-socket that binds to the masters publishing interface tries @@ -154,9 +181,8 @@ id: {{ minion['id'] }} # minions reconnect immediately which might flood the master (the ZeroMQ-default # is usually a 100ms delay). To prevent this, these three recon_* settings # can be used. -# # recon_default: the interval in milliseconds that the socket should wait before -# trying to reconnect to the master (100ms = 1 second) +# trying to reconnect to the master (1000ms = 1 second) # # recon_max: the maximum time a socket should wait. each interval the time to wait # is calculated by doubling the previous time. if recon_max is reached, @@ -175,23 +201,20 @@ id: {{ minion['id'] }} # and recon_max value kind of defeats the purpose of being able to # change these settings. If all minions have the same values and your # setup is quite large (several thousand minions), they will still -# flood the master. The desired behaviour is to have timeframe within +# flood the master. The desired behavior is to have timeframe within # all minions try to reconnect. - -# Example on how to use these settings: -# The goal: have all minions reconnect within a 60 second timeframe on a disconnect # -# The settings: -#recon_default: 1000 -#recon_max: 59000 -#recon_randomize: True +# Example on how to use these settings. The goal: have all minions reconnect within a +# 60 second timeframe on a disconnect. +# recon_default: 1000 +# recon_max: 59000 +# recon_randomize: True # # Each minion will have a randomized reconnect value between 'recon_default' # and 'recon_default + recon_max', which in this example means between 1000ms # 60000ms (or between 1 and 60 seconds). The generated random-value will be # doubled after each attempt to reconnect. Lets say the generated random # value is 11 seconds (or 11000ms). -# # reconnect 1: wait 11 seconds # reconnect 2: wait 22 seconds # reconnect 3: wait 33 seconds @@ -236,20 +259,25 @@ id: {{ minion['id'] }} # is not enabled. {{ get_config('grains_cache_expiration', '300') }} - -# When healing, a dns_check is run. This is to make sure that the originally -# resolved dns has not changed. If this is something that does not happen in -# your environment, set this value to False. -{{ get_config('dns_check', 'True') }} - # Windows platforms lack posix IPC and must rely on slower TCP based inter- # process communications. Set ipc_mode to 'tcp' on such systems {{ get_config('ipc_mode', 'ipc') }} -# + # Overwrite the default tcp ports used by the minion when in tcp mode {{ get_config('tcp_pub_port', '4510') }} {{ get_config('tcp_pull_port', '4511') }} +# Passing very large events can cause the minion to consume large amounts of +# memory. This value tunes the maximum size of a message allowed onto the +# minion event bus. The value is expressed in bytes. +{{ get_config('max_event_size', '1048576') }} + +# To detect failed master(s) and fire events on connect/disconnect, set +# master_alive_interval to the number of seconds to poll the masters for +# connection events. +# +{{ get_config('master_alive_interval', '30') }} + # The minion can include configuration from other files. To enable this, # pass a list of paths to this option. The paths can be either relative or # absolute; if relative, they are considered to be relative to the directory @@ -257,7 +285,6 @@ id: {{ minion['id'] }} # of shell-style globbing. If no files are matched by a path passed to this # option then the minion will log a warning message. # -# # Include a config file from some other path: # include: /etc/salt/extra_config # @@ -265,30 +292,31 @@ id: {{ minion['id'] }} #include: # - /etc/salt/extra_config # - /etc/roles/webserver -{% if 'include' in minion -%} - {% if isinstance(minion['include'], list) -%} +{% if 'include' in cfg_minion -%} + {% if isinstance(cfg_minion['include'], list) -%} include: - {% for include in minion['include'] -%} + {% for include in cfg_minion['include'] -%} - {{ include }} {% endfor -%} {% else -%} - include: minion['include'] + include: cfg_minion['include'] {% endif -%} -{% elif 'include' in salt -%} - {% if isinstance(salt['include'], list) -%} +{% elif 'include' in cfg_salt -%} + {% if isinstance(cfg_salt['include'], list) -%} include: - {% for include in salt['include'] -%} + {% for include in cfg_salt['include'] -%} - {{ include }} {% endfor -%} {% else -%} - include: salt['include'] + include: cfg_salt['include'] {% endif -%} {% endif -%} + ##### Minion module management ##### ########################################## # Disable specific modules. This allows the admin to limit the level of -# access the master has to the minion +# access the master has to the minion. {{ get_config('disable_modules', '[cmd,test]') }} {{ get_config('disable_returners', '[]') }} # @@ -300,12 +328,12 @@ id: {{ minion['id'] }} {{ get_config('returner_dirs', '[]') }} {{ get_config('states_dirs', '[]') }} {{ get_config('render_dirs', '[]') }} +{{ get_config('utils_dirs', '[]') }} # # A module provider can be statically overwritten or extended for the minion # via the providers option, in this case the default module will be # overwritten by the specified module. In this example the pkg module will # be provided by the yumpkg5 module instead of the system default. -# #providers: # pkg: yumpkg5 {{ get_config('providers', '{}') }} @@ -313,14 +341,11 @@ id: {{ minion['id'] }} # Enable Cython modules searching and loading. (Default: False) {{ get_config('cython_enable', 'False') }} # -# -# -# Specify a max size (in bytes) for modules on import -# this feature is currently only supported on *nix OSs and requires psutil +# Specify a max size (in bytes) for modules on import. This feature is currently +# only supported on *nix operating systems and requires psutil. {{ get_config('modules_max_memory', '-1') }} - ##### State Management Settings ##### ########################################### # The state management system executes all of the state templates on the minion @@ -338,21 +363,21 @@ id: {{ minion['id'] }} {{ get_config('renderer', 'yaml_jinja') }} # # The failhard option tells the minions to stop immediately after the first -# failure detected in the state execution, defaults to False +# failure detected in the state execution. Defaults to False. {{ get_config('failhard', 'False') }} # -# autoload_dynamic_modules Turns on automatic loading of modules found in the -# environments on the master. This is turned on by default, to turn of -# autoloading modules when states run set this value to False +# autoload_dynamic_modules turns on automatic loading of modules found in the +# environments on the master. This is turned on by default. To turn of +# autoloading modules when states run, set this value to False. {{ get_config('autoload_dynamic_modules', 'True') }} # # clean_dynamic_modules keeps the dynamic modules on the minion in sync with # the dynamic modules on the master, this means that if a dynamic module is -# not on the master it will be deleted from the minion. By default this is -# enabled and can be disabled by changing this value to False +# not on the master it will be deleted from the minion. By default, this is +# enabled and can be disabled by changing this value to False. {{ get_config('clean_dynamic_modules', 'True') }} # -# Normally the minion is not isolated to any single environment on the master +# Normally, the minion is not isolated to any single environment on the master # when running states, but the environment can be isolated on the minion side # by statically setting it. Remember that the recommended way to manage # environments is to isolate via the top file. @@ -368,14 +393,24 @@ id: {{ minion['id'] }} # 'top' -- Read top_file option and execute based on that file on the Master {{ get_config('startup_states', "''") }} # -# list of states to run when the minion starts up if startup_states is 'sls' +# List of states to run when the minion starts up if startup_states is 'sls': #sls_list: # - edit.vim # - hyper {{ get_config('sls_list', '[]') }} -# top file to execute if startup_states is 'top' +# +# Top file to execute if startup_states is 'top': {{ get_config('top_file', "''") }} +# Automatically aggregate all states that have support for mod_aggregate by +# setting to True. Or pass a list of state module names to automatically +# aggregate just those types. +# +# state_aggregate: +# - pkg +# +#state_aggregate: False + ##### File Directory Settings ##### ########################################## # The Salt Minion can redirect all file server operations to a local directory, @@ -386,7 +421,11 @@ id: {{ minion['id'] }} # Set the file client. The client defaults to looking on the master server for # files, but can be directed to look at the local file directory setting # defined below by setting it to local. +{%- if standalone %} +file_client: local +{%- else %} {{ get_config('file_client', 'remote') }} +{%- endif %} # The file directory works on environments passed to the minion, each environment # can have multiple root directories, the subdirectories in the multiple file @@ -403,23 +442,13 @@ id: {{ minion['id'] }} # - /srv/salt/prod/services # - /srv/salt/prod/states # -{% if 'file_roots' in minion -%} -file_roots: -{%- for name, roots in minion['file_roots']|dictsort %} - {{ name }}: -{%- for dir in roots %} - - {{ dir }} -{%- endfor -%} -{%- endfor -%} -{% elif 'file_roots' in salt -%} -file_roots: -{%- for name, roots in salt['file_roots']|dictsort %} - {{ name }}: -{%- for dir in roots %} - - {{ dir }} -{%- endfor -%} -{%- endfor -%} -{% else -%} +{% if 'file_roots' in cfg_minion -%} +{{ file_roots(cfg_minion['file_roots']) }} +{%- elif 'file_roots' in cfg_salt -%} +{{ file_roots(cfg_salt['file_roots']) }} +{%- elif formulas|length -%} +{{ file_roots({'base': ['/srv/salt']}) }} +{%- else -%} #file_roots: # base: # - /srv/salt @@ -429,31 +458,82 @@ file_roots: # to attempt to find files. To limit this behavior so that the fileserver only # traverses directories with SLS files and special Salt directories like _modules, # enable the option below. This might be useful for installations where a file root -# has a very large number of files and performance is negatively impacted. -# -# Default is False. -# +# has a very large number of files and performance is negatively impacted. Default +# is False. {{ get_config('fileserver_limit_traversal', 'False') }} +# +# Git fileserver backend configuration +# +# Gitfs can be provided by one of two python modules: GitPython or pygit2. If +# using pygit2, both libgit2 and git must also be installed. +{{ get_config('gitfs_provider', 'pygit2') }} +# +# When using the git fileserver backend at least one git remote needs to be +# defined. The user running the salt master will need read access to the repo. +# +# The repos will be searched in order to find the file requested by a client +# and the first repo to have the file will return it. +# When using the git backend branches and tags are translated into salt +# environments. +# Note: file:// repos will be treated as a remote, so refs you want used must +# exist in that repo as *local* refs. +{% if 'gitfs_remotes' in cfg_minion -%} +gitfs_remotes: +{%- for remote in cfg_minion['gitfs_remotes'] %} +{%- if remote is iterable and remote is not string %} + {%- for repo, children in remote.items() %} + - {{ repo }}: + {%- for child in children %} + {%- for key, value in child.items() %} + - {{ key }}: {{ value }} + {%- endfor -%} + {%- endfor -%} + {%- endfor -%} + {%- else %} + - {{ remote }} +{%- endif -%} +{%- endfor -%} +{%- endif %} +# +#gitfs_remotes: +# - git://github.com/saltstack/salt-states.git +# - file:///var/git/saltmaster +# +# The gitfs_ssl_verify option specifies whether to ignore ssl certificate +# errors when contacting the gitfs backend. You might want to set this to +# false if you're using a git backend that uses a self-signed certificate but +# keep in mind that setting this flag to anything other than the default of True +# is a security concern, you may want to try using the ssh transport. +{{ get_config('gitfs_ssl_verify', 'True') }} + +# The gitfs_root option gives the ability to serve files from a subdirectory +# within the repository. The path is defined relative to the root of the +# repository and defaults to the repository root. +{{ get_config('gitfs_root', 'somefolder/otherfolder') }} + # The hash_type is the hash to use when discovering the hash of a file in # the local fileserver. The default is md5, but sha1, sha224, sha256, sha384 # and sha512 are also supported. +# +# Warning: Prior to changing this value, the minion should be stopped and all +# Salt caches should be cleared. {{ get_config('hash_type', 'md5') }} # The Salt pillar is searched for locally if file_client is set to local. If # this is the case, and pillar data is defined, then the pillar_roots need to # also be configured on the minion: -{% if 'pillar_roots' in minion -%} +{% if 'pillar_roots' in cfg_minion -%} pillar_roots: -{%- for name, roots in minion['pillar_roots']|dictsort %} +{%- for name, roots in cfg_minion['pillar_roots']|dictsort %} {{ name }}: {%- for dir in roots %} - {{ dir }} {%- endfor -%} {%- endfor -%} -{% elif 'pillar_roots' in salt -%} +{% elif 'pillar_roots' in cfg_salt -%} pillar_roots: -{%- for name, roots in salt['pillar_roots']|dictsort %} +{%- for name, roots in cfg_salt['pillar_roots']|dictsort %} {{ name }}: {%- for dir in roots %} - {{ dir }} @@ -465,6 +545,7 @@ pillar_roots: # - /srv/salt {%- endif %} + ###### Security settings ##### ########################################### # Enable "open mode", this mode still maintains encryption, but turns off @@ -484,12 +565,17 @@ pillar_roots: # The state_verbose setting can be set to True or False, when set to False # all data that has a result of True and no changes will be suppressed. {{ get_config('state_verbose', 'True') }} -# + # The state_output setting changes if the output is the full multi line # output for each changed state if set to 'full', but if set to 'terse' # the output will be shortened to a single line. {{ get_config('state_output', 'full') }} -# + +# The state_output_diff setting changes whether or not the output from +# successful states is returned. Useful when even the terse output of these +# states is cluttering the logs. Set it to True to ignore them. +{{ get_config('state_output_diff', 'False') }} + # Fingerprint of the master public key to double verify the master is valid, # the master fingerprint can be found by running "salt-key -F master" on the # salt master. @@ -501,6 +587,7 @@ pillar_roots: # publication a new process is spawned and the command is executed therein. {{ get_config('multiprocessing', 'True') }} + ##### Logging settings ##### ########################################## # The location of the minion log file @@ -514,14 +601,15 @@ pillar_roots: # {{ get_config('log_file', '/var/log/salt/minion') }} {{ get_config('key_logfile', ' /var/log/salt/key') }} -# + # The level of messages to send to the console. # One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. # Default: 'warning' {{ get_config('log_level', 'warning') }} -# + # The level of messages to send to the log file. # One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# If using 'log_granular_levels' this must be set to the highest desired level. # Default: 'warning' {{ get_config('log_level_logfile', '') }} @@ -529,21 +617,22 @@ pillar_roots: # can be seen here: http://docs.python.org/library/time.html#time.strftime {{ get_config('log_datefmt', "'%H:%M:%S'") }} {{ get_config('log_datefmt_logfile', "'%Y-%m-%d %H:%M:%S'") }} -# + # The format of the console logging messages. Allowed formatting options can # be seen here: http://docs.python.org/library/logging.html#logrecord-attributes {{ get_config('log_fmt_console', "'[%(levelname)-8s] %(message)s'") }} {{ get_config('log_fmt_logfile', "'%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'") }} -# + # This can be used to control logging levels more specificically. This # example sets the main salt library at the 'warning' level, but sets # 'salt.modules' to log at the 'debug' level: # log_granular_levels: -# 'salt': 'warning', +# 'salt': 'warning' # 'salt.modules': 'debug' # {{ get_config('log_granular_levels', '{}') }} + ###### Module configuration ##### ########################################### # Salt allows for modules to be passed arbitrary configuration data, any data @@ -563,8 +652,8 @@ pillar_roots: # # A dict for the test module: #test.baz: {spam: sausage, cheese: bread} -{%- if 'module_config' in minion %} -{%- for modkey, modval in minion.module_config.items() %} +{%- if 'module_config' in cfg_minion %} +{%- for modkey, modval in cfg_minion.module_config.items() %} {{ modkey }}: {{ modval }} {%- endfor %} {%- endif %} @@ -591,20 +680,20 @@ pillar_roots: # the risk that it could tear down the connection the master and minion # without informing either party that their connection has been taken away. # Enabling TCP Keepalives prevents this from happening. -# + # Overall state of TCP Keepalives, enable (1 or True), disable (0 or False) # or leave to the OS defaults (-1), on Linux, typically disabled. Default True, enabled. {{ get_config('tcp_keepalive', 'True') }} -# + # How long before the first keepalive should be sent in seconds. Default 300 # to send the first keepalive after 5 minutes, OS default (-1) is typically 7200 seconds # on Linux see /proc/sys/net/ipv4/tcp_keepalive_time. {{ get_config('tcp_keepalive_idle', '300') }} -# + # How many lost probes are needed to consider the connection lost. Default -1 # to use OS defaults, typically 9 on Linux, see /proc/sys/net/ipv4/tcp_keepalive_probes. {{ get_config('tcp_keepalive_cnt', '-1') }} -# + # How often, in seconds, to send keepalives after the first one. Default -1 to # use OS defaults, typically 75 seconds on Linux, see # /proc/sys/net/ipv4/tcp_keepalive_intvl. @@ -613,5 +702,10 @@ pillar_roots: ###### Windows Software settings ###### ############################################ -# Location of the repository cache file on the master +# Location of the repository cache file on the master: {{ get_config('win_repo_cachefile', 'salt://win/repo/winrepo.p') }} + +###### Returner settings ###### +############################################ +# Which returner(s) will be used for minion's result: +#return: mysql diff --git a/salt/formulas.jinja b/salt/formulas.jinja new file mode 100644 index 000000000..071312c1a --- /dev/null +++ b/salt/formulas.jinja @@ -0,0 +1,52 @@ +{% set defaults = { + 'baseurl': 'https://github.com/saltstack-formulas', + 'basedir': '/srv/formulas', + 'update': False, + 'options': {}, + } +%} +{% set formulas = salt['pillar.get']('salt_formulas:list', {}) %} + +{%- macro formulas_git_opt(env, opt) -%} +{%- set value = salt['pillar.get']('salt_formulas:git_opts:{}:{}'.format(env, opt), + salt['pillar.get']('salt_formulas:git_opts:default:{}'.format(opt), + defaults[opt])) -%} +{%- if value is mapping -%} +{{ value|yaml }} +{%- else -%} +{{ value }} +{%- endif -%} +{%- endmacro -%} + +{%- macro formulas_roots(env) -%} +{%- set value = [] -%} +{%- for dir in formulas.get(env, []) -%} +{%- do value.append('{}/{}'.format(formulas_git_opt(env, 'basedir'), dir)) -%} +{%- endfor -%} +{{ value|yaml }} +{%- endmacro -%} + +{# Generate file_roots config merging standard salt config and list of + enabled formulas #} +{%- macro file_roots(input) -%} +{%- set processed_envs = [] -%} +file_roots: +{%- for name, roots in input|dictsort -%} +{%- do processed_envs.append(name) %} + {{ name }}: +{%- for dir in roots %} + - {{ dir }} +{%- endfor -%} +{%- for dir in formulas_roots(name)|load_yaml %} + - {{ dir }} +{%- endfor -%} +{%- endfor -%} +{%- for name in formulas -%} +{%- if name not in processed_envs %} + {{ name }}: +{%- for dir in formulas_roots(name)|load_yaml %} + - {{ dir }} +{%- endfor -%} +{%- endif -%} +{%- endfor -%} +{%- endmacro -%} diff --git a/salt/formulas.sls b/salt/formulas.sls new file mode 100644 index 000000000..55c31fe99 --- /dev/null +++ b/salt/formulas.sls @@ -0,0 +1,44 @@ +{% set processed_gitdirs = [] %} +{% set processed_basedirs = [] %} + +{% from "salt/formulas.jinja" import formulas_git_opt with context %} + +# Loop over all formulas listed in pillar data +{% for env, entries in salt['pillar.get']('salt_formulas:list').iteritems() %} +{% for entry in entries %} + +{% set basedir = formulas_git_opt(env, 'basedir') %} +{% set gitdir = '{}/{}'.format(basedir, entry) %} +{% set update = formulas_git_opt(env, 'update')|load_yaml %} + +# Setup the directory hosting the Git repository +{% if basedir not in processed_basedirs %} +{% do processed_basedirs.append(basedir) %} +{{ basedir }}: + file.directory: + {%- for key, value in salt['pillar.get']('salt_formulas:basedir_opts', + {'makedirs': True}).iteritems() %} + - {{ key }}: {{ value }} + {%- endfor %} +{% endif %} + +# Setup the formula Git repository +{% if gitdir not in processed_gitdirs %} +{% do processed_gitdirs.append(gitdir) %} +{% set options = formulas_git_opt(env, 'options')|load_yaml %} +{{ gitdir }}: + git.latest: + - name: {{ formulas_git_opt(env, 'baseurl') }}/{{ entry }}.git + - target: {{ gitdir }} + {%- for key, value in options.iteritems() %} + - {{ key }}: {{ value }} + {%- endfor %} + - require: + - file: {{ basedir }} + {%- if not update %} + - unless: test -e {{ gitdir }} + {%- endif %} +{% endif %} + +{% endfor %} +{% endfor %} diff --git a/salt/minion.sls b/salt/minion.sls index 15b4f7599..a80e210ac 100644 --- a/salt/minion.sls +++ b/salt/minion.sls @@ -8,6 +8,8 @@ salt-minion: - template: jinja - source: salt://salt/files/minion.d - clean: True + - context: + standalone: False service.running: - enable: True - name: {{ pkgs.get('minion-service', 'salt-minion') }} diff --git a/salt/package-map.jinja b/salt/package-map.jinja index 38f3888eb..770de38a0 100644 --- a/salt/package-map.jinja +++ b/salt/package-map.jinja @@ -37,6 +37,10 @@ 'salt-minion': 'salt', 'salt-syndic': 'salt', 'salt-cloud': 'salt'}, + 'openSUSE':{'salt-master': 'salt-master', + 'salt-minion': 'salt-minion', + 'salt-syndic': 'salt-syndic', + 'salt-cloud': 'salt-cloud'}, 'FreeBSD': {'salt-master': 'py27-salt', 'salt-minion': 'py27-salt', 'salt-syndic': 'py27-salt', diff --git a/salt/pkgrepo/absent.sls b/salt/pkgrepo/absent.sls new file mode 100644 index 000000000..d79b15f0e --- /dev/null +++ b/salt/pkgrepo/absent.sls @@ -0,0 +1,2 @@ +include: + - .{{ grains['os']|lower }}.absent diff --git a/salt/pkgrepo/debian/absent.sls b/salt/pkgrepo/debian/absent.sls new file mode 100644 index 000000000..2ace19381 --- /dev/null +++ b/salt/pkgrepo/debian/absent.sls @@ -0,0 +1,7 @@ +drop-saltstack-pkgrepo: + file.absent: + - name: /etc/apt/sources.list.d/saltstack.list + +drop-saltstack-apt-key: + file.absent: + - name: /etc/apt/trusted.gpg.d/saltstack.gpg diff --git a/salt/pkgrepo/debian/init.sls b/salt/pkgrepo/debian/init.sls new file mode 100644 index 000000000..ffe716566 --- /dev/null +++ b/salt/pkgrepo/debian/init.sls @@ -0,0 +1,21 @@ +saltstack-apt-key: + file.managed: + - name: /etc/apt/trusted.gpg.d/saltstack.gpg + - source: salt://salt/pkgrepo/debian/saltstack.gpg + - user: root + - group: root + - mode: 644 + +saltstack-pkgrepo: + file.managed: + - name: /etc/apt/sources.list.d/saltstack.list + - source: salt://salt/pkgrepo/debian/sources.list + - user: root + - group: root + - mode: 644 + - template: jinja + - require: + - file: saltstack-apt-key + # Order: 1 because we can't put a require_in on "pkg: salt-{master,minion}" + # because we don't know if they are used. + - order: 1 diff --git a/salt/pkgrepo/debian/saltstack.gpg b/salt/pkgrepo/debian/saltstack.gpg new file mode 100644 index 000000000..78da01e4d Binary files /dev/null and b/salt/pkgrepo/debian/saltstack.gpg differ diff --git a/salt/pkgrepo/debian/sources.list b/salt/pkgrepo/debian/sources.list new file mode 100644 index 000000000..f99e4263f --- /dev/null +++ b/salt/pkgrepo/debian/sources.list @@ -0,0 +1,2 @@ +# saltstack +deb http://debian.saltstack.com/debian {{ grains['oscodename'] }}-saltstack main diff --git a/salt/pkgrepo/init.sls b/salt/pkgrepo/init.sls new file mode 100644 index 000000000..0d41bc4bb --- /dev/null +++ b/salt/pkgrepo/init.sls @@ -0,0 +1,2 @@ +include: + - .{{ grains['os']|lower }} diff --git a/salt/pkgrepo/ubuntu/absent.sls b/salt/pkgrepo/ubuntu/absent.sls new file mode 100644 index 000000000..27b5fd69b --- /dev/null +++ b/salt/pkgrepo/ubuntu/absent.sls @@ -0,0 +1,3 @@ +drop-saltstack-pkgrepo: + pkgrepo.absent: + - ppa: saltstack/salt diff --git a/salt/pkgrepo/ubuntu/init.sls b/salt/pkgrepo/ubuntu/init.sls new file mode 100644 index 000000000..bd3986ab1 --- /dev/null +++ b/salt/pkgrepo/ubuntu/init.sls @@ -0,0 +1,3 @@ +saltstack-pkgrepo: + pkgrepo.managed: + - ppa: saltstack/salt diff --git a/salt/standalone.sls b/salt/standalone.sls new file mode 100644 index 000000000..e33da85ea --- /dev/null +++ b/salt/standalone.sls @@ -0,0 +1,18 @@ +{% from "salt/package-map.jinja" import pkgs with context %} + +salt-minion: + pkg.installed: + - name: {{ pkgs['salt-minion'] }} + file.recurse: + - name: {{ pkgs.get('config-path', '/etc/salt') }}/minion.d + - template: jinja + - source: salt://salt/files/minion.d + - clean: True + - context: + standalone: True + service.dead: + - enable: False + - name: {{ pkgs.get('minion-service', 'salt-minion') }} + - require: + - pkg: salt-minion + - file: salt-minion