1""" 2All salt configuration loading and defaults should be in this module 3""" 4import codecs 5import glob 6import logging 7import os 8import re 9import sys 10import time 11import types 12import urllib.parse 13from copy import deepcopy 14 15import salt.defaults.exitcodes 16import salt.exceptions 17import salt.syspaths 18import salt.utils.data 19import salt.utils.dictupdate 20import salt.utils.files 21import salt.utils.immutabletypes as immutabletypes 22import salt.utils.network 23import salt.utils.path 24import salt.utils.platform 25import salt.utils.stringutils 26import salt.utils.user 27import salt.utils.validate.path 28import salt.utils.xdg 29import salt.utils.yaml 30import salt.utils.zeromq 31 32try: 33 import psutil 34 35 if not hasattr(psutil, "virtual_memory"): 36 raise ImportError("Version of psutil too old.") 37 HAS_PSUTIL = True 38except ImportError: 39 HAS_PSUTIL = False 40 41log = logging.getLogger(__name__) 42 43_DFLT_LOG_DATEFMT = "%H:%M:%S" 44_DFLT_LOG_DATEFMT_LOGFILE = "%Y-%m-%d %H:%M:%S" 45_DFLT_LOG_FMT_CONSOLE = "[%(levelname)-8s] %(message)s" 46_DFLT_LOG_FMT_LOGFILE = ( 47 "%(asctime)s,%(msecs)03d [%(name)-17s:%(lineno)-4d][%(levelname)-8s][%(process)d]" 48 " %(message)s" 49) 50_DFLT_LOG_FMT_JID = "[JID: %(jid)s]" 51_DFLT_REFSPECS = ["+refs/heads/*:refs/remotes/origin/*", "+refs/tags/*:refs/tags/*"] 52DEFAULT_INTERVAL = 60 53 54if salt.utils.platform.is_windows(): 55 # Since an 'ipc_mode' of 'ipc' will never work on Windows due to lack of 56 # support in ZeroMQ, we want the default to be something that has a 57 # chance of working. 58 _DFLT_IPC_MODE = "tcp" 59 _DFLT_FQDNS_GRAINS = False 60 _MASTER_TRIES = -1 61 # This needs to be SYSTEM in order for salt-master to run as a Service 62 # Otherwise, it will not respond to CLI calls 63 _MASTER_USER = "SYSTEM" 64elif salt.utils.platform.is_proxy(): 65 _DFLT_IPC_MODE = "ipc" 66 _DFLT_FQDNS_GRAINS = False 67 _MASTER_TRIES = 1 68 _MASTER_USER = salt.utils.user.get_user() 69else: 70 _DFLT_IPC_MODE = "ipc" 71 _DFLT_FQDNS_GRAINS = True 72 _MASTER_TRIES = 1 73 _MASTER_USER = salt.utils.user.get_user() 74 75 76def _gather_buffer_space(): 77 """ 78 Gather some system data and then calculate 79 buffer space. 80 81 Result is in bytes. 82 """ 83 if HAS_PSUTIL and psutil.version_info >= (0, 6, 0): 84 # Oh good, we have psutil. This will be quick. 85 total_mem = psutil.virtual_memory().total 86 else: 87 # Avoid loading core grains unless absolutely required 88 import platform 89 import salt.grains.core 90 91 # We need to load up ``mem_total`` grain. Let's mimic required OS data. 92 os_data = {"kernel": platform.system()} 93 grains = salt.grains.core._memdata(os_data) 94 total_mem = grains["mem_total"] 95 # Return the higher number between 5% of the system memory and 10MiB 96 return max([total_mem * 0.05, 10 << 20]) 97 98 99# For the time being this will be a fixed calculation 100# TODO: Allow user configuration 101_DFLT_IPC_WBUFFER = _gather_buffer_space() * 0.5 102# TODO: Reserved for future use 103_DFLT_IPC_RBUFFER = _gather_buffer_space() * 0.5 104 105VALID_OPTS = immutabletypes.freeze( 106 { 107 # The address of the salt master. May be specified as IP address or hostname 108 "master": (str, list), 109 # The TCP/UDP port of the master to connect to in order to listen to publications 110 "master_port": (str, int), 111 # The behaviour of the minion when connecting to a master. Can specify 'failover', 112 # 'disable', 'distributed', or 'func'. If 'func' is specified, the 'master' option should be 113 # set to an exec module function to run to determine the master hostname. If 'disable' is 114 # specified the minion will run, but will not try to connect to a master. If 'distributed' 115 # is specified the minion will try to deterministically pick a master based on its' id. 116 "master_type": str, 117 # Specify the format in which the master address will be specified. Can 118 # specify 'default' or 'ip_only'. If 'ip_only' is specified, then the 119 # master address will not be split into IP and PORT. 120 "master_uri_format": str, 121 # The following optiosn refer to the Minion only, and they specify 122 # the details of the source address / port to be used when connecting to 123 # the Master. This is useful when dealing withmachines where due to firewall 124 # rules you are restricted to use a certain IP/port combination only. 125 "source_interface_name": str, 126 "source_address": str, 127 "source_ret_port": (str, int), 128 "source_publish_port": (str, int), 129 # The fingerprint of the master key may be specified to increase security. Generate 130 # a master fingerprint with `salt-key -F master` 131 "master_finger": str, 132 # Deprecated in 2019.2.0. Use 'random_master' instead. 133 # Do not remove! Keep as an alias for usability. 134 "master_shuffle": bool, 135 # When in multi-master mode, temporarily remove a master from the list if a conenction 136 # is interrupted and try another master in the list. 137 "master_alive_interval": int, 138 # When in multi-master failover mode, fail back to the first master in the list if it's back 139 # online. 140 "master_failback": bool, 141 # When in multi-master mode, and master_failback is enabled ping the top master with this 142 # interval. 143 "master_failback_interval": int, 144 # The name of the signing key-pair 145 "master_sign_key_name": str, 146 # Sign the master auth-replies with a cryptographic signature of the masters public key. 147 "master_sign_pubkey": bool, 148 # Enables verification of the master-public-signature returned by the master in auth-replies. 149 # Must also set master_sign_pubkey for this to work 150 "verify_master_pubkey_sign": bool, 151 # If verify_master_pubkey_sign is enabled, the signature is only verified, if the public-key of 152 # the master changes. If the signature should always be verified, this can be set to True. 153 "always_verify_signature": bool, 154 # The name of the file in the masters pki-directory that holds the pre-calculated signature of 155 # the masters public-key 156 "master_pubkey_signature": str, 157 # Instead of computing the signature for each auth-reply, use a pre-calculated signature. 158 # The master_pubkey_signature must also be set for this. 159 "master_use_pubkey_signature": bool, 160 # Enable master stats eveents to be fired, these events will contain information about 161 # what commands the master is processing and what the rates are of the executions 162 "master_stats": bool, 163 "master_stats_event_iter": int, 164 # The key fingerprint of the higher-level master for the syndic to verify it is talking to the 165 # intended master 166 "syndic_finger": str, 167 # The caching mechanism to use for the PKI key store. Can substantially decrease master publish 168 # times. Available types: 169 # 'maint': Runs on a schedule as a part of the maintanence process. 170 # '': Disable the key cache [default] 171 "key_cache": str, 172 # The user under which the daemon should run 173 "user": str, 174 # The root directory prepended to these options: pki_dir, cachedir, 175 # sock_dir, log_file, autosign_file, autoreject_file, extension_modules, 176 # key_logfile, pidfile: 177 "root_dir": str, 178 # The directory used to store public key data 179 "pki_dir": str, 180 # A unique identifier for this daemon 181 "id": str, 182 # Use a module function to determine the unique identifier. If this is 183 # set and 'id' is not set, it will allow invocation of a module function 184 # to determine the value of 'id'. For simple invocations without function 185 # arguments, this may be a string that is the function name. For 186 # invocations with function arguments, this may be a dictionary with the 187 # key being the function name, and the value being an embedded dictionary 188 # where each key is a function argument name and each value is the 189 # corresponding argument value. 190 "id_function": (dict, str), 191 # The directory to store all cache files. 192 "cachedir": str, 193 # Append minion_id to these directories. Helps with 194 # multiple proxies and minions running on the same machine. 195 # Allowed elements in the list: pki_dir, cachedir, extension_modules, pidfile 196 "append_minionid_config_dirs": list, 197 # Flag to cache jobs locally. 198 "cache_jobs": bool, 199 # The path to the salt configuration file 200 "conf_file": str, 201 # The directory containing unix sockets for things like the event bus 202 "sock_dir": str, 203 # The pool size of unix sockets, it is necessary to avoid blocking waiting for zeromq and tcp communications. 204 "sock_pool_size": int, 205 # Specifies how the file server should backup files, if enabled. The backups 206 # live in the cache dir. 207 "backup_mode": str, 208 # A default renderer for all operations on this host 209 "renderer": str, 210 # Renderer whitelist. The only renderers from this list are allowed. 211 "renderer_whitelist": list, 212 # Rendrerer blacklist. Renderers from this list are disalloed even if specified in whitelist. 213 "renderer_blacklist": list, 214 # A flag indicating that a highstate run should immediately cease if a failure occurs. 215 "failhard": bool, 216 # A flag to indicate that highstate runs should force refresh the modules prior to execution 217 "autoload_dynamic_modules": bool, 218 # Force the minion into a single environment when it fetches files from the master 219 "saltenv": (type(None), str), 220 # Prevent saltenv from being overridden on the command line 221 "lock_saltenv": bool, 222 # Force the minion into a single pillar root when it fetches pillar data from the master 223 "pillarenv": (type(None), str), 224 # Make the pillarenv always match the effective saltenv 225 "pillarenv_from_saltenv": bool, 226 # Allows a user to provide an alternate name for top.sls 227 "state_top": str, 228 "state_top_saltenv": (type(None), str), 229 # States to run when a minion starts up 230 "startup_states": str, 231 # List of startup states 232 "sls_list": list, 233 # Configuration for snapper in the state system 234 "snapper_states": bool, 235 "snapper_states_config": str, 236 # A top file to execute if startup_states == 'top' 237 "top_file": str, 238 # Location of the files a minion should look for. Set to 'local' to never ask the master. 239 "file_client": str, 240 "local": bool, 241 # When using a local file_client, this parameter is used to allow the client to connect to 242 # a master for remote execution. 243 "use_master_when_local": bool, 244 # A map of saltenvs and fileserver backend locations 245 "file_roots": dict, 246 # A map of saltenvs and fileserver backend locations 247 "pillar_roots": dict, 248 # The external pillars permitted to be used on-demand using pillar.ext 249 "on_demand_ext_pillar": list, 250 # A map of glob paths to be used 251 "decrypt_pillar": list, 252 # Delimiter to use in path expressions for decrypt_pillar 253 "decrypt_pillar_delimiter": str, 254 # Default renderer for decrypt_pillar 255 "decrypt_pillar_default": str, 256 # List of renderers available for decrypt_pillar 257 "decrypt_pillar_renderers": list, 258 # The type of hashing algorithm to use when doing file comparisons 259 "hash_type": str, 260 # Order of preference for optimized .pyc files (PY3 only) 261 "optimization_order": list, 262 # Refuse to load these modules 263 "disable_modules": list, 264 # Refuse to load these returners 265 "disable_returners": list, 266 # Tell the loader to only load modules in this list 267 "whitelist_modules": list, 268 # A list of additional directories to search for salt modules in 269 "module_dirs": list, 270 # A list of additional directories to search for salt returners in 271 "returner_dirs": list, 272 # A list of additional directories to search for salt states in 273 "states_dirs": list, 274 # A list of additional directories to search for salt grains in 275 "grains_dirs": list, 276 # A list of additional directories to search for salt renderers in 277 "render_dirs": list, 278 # A list of additional directories to search for salt outputters in 279 "outputter_dirs": list, 280 # A list of additional directories to search for salt utilities in. (Used by the loader 281 # to populate __utils__) 282 "utils_dirs": list, 283 # salt cloud providers 284 "providers": dict, 285 # First remove all modules during any sync operation 286 "clean_dynamic_modules": bool, 287 # A flag indicating that a master should accept any minion connection without any authentication 288 "open_mode": bool, 289 # Whether or not processes should be forked when needed. The alternative is to use threading. 290 "multiprocessing": bool, 291 # Maximum number of concurrently active processes at any given point in time 292 "process_count_max": int, 293 # Whether or not the salt minion should run scheduled mine updates 294 "mine_enabled": bool, 295 # Whether or not scheduled mine updates should be accompanied by a job return for the job cache 296 "mine_return_job": bool, 297 # The number of minutes between mine updates. 298 "mine_interval": int, 299 # The ipc strategy. (i.e., sockets versus tcp, etc) 300 "ipc_mode": str, 301 # Enable ipv6 support for daemons 302 "ipv6": (type(None), bool), 303 # The chunk size to use when streaming files with the file server 304 "file_buffer_size": int, 305 # The TCP port on which minion events should be published if ipc_mode is TCP 306 "tcp_pub_port": int, 307 # The TCP port on which minion events should be pulled if ipc_mode is TCP 308 "tcp_pull_port": int, 309 # The TCP port on which events for the master should be published if ipc_mode is TCP 310 "tcp_master_pub_port": int, 311 # The TCP port on which events for the master should be pulled if ipc_mode is TCP 312 "tcp_master_pull_port": int, 313 # The TCP port on which events for the master should pulled and then republished onto 314 # the event bus on the master 315 "tcp_master_publish_pull": int, 316 # The TCP port for mworkers to connect to on the master 317 "tcp_master_workers": int, 318 # The file to send logging data to 319 "log_file": str, 320 # The level of verbosity at which to log 321 "log_level": str, 322 # The log level to log to a given file 323 "log_level_logfile": (type(None), str), 324 # The format to construct dates in log files 325 "log_datefmt": str, 326 # The dateformat for a given logfile 327 "log_datefmt_logfile": str, 328 # The format for console logs 329 "log_fmt_console": str, 330 # The format for a given log file 331 "log_fmt_logfile": (tuple, str), 332 # A dictionary of logging levels 333 "log_granular_levels": dict, 334 # The maximum number of bytes a single log file may contain before 335 # it is rotated. A value of 0 disables this feature. 336 # Currently only supported on Windows. On other platforms, use an 337 # external tool such as 'logrotate' to manage log files. 338 "log_rotate_max_bytes": int, 339 # The number of backup files to keep when rotating log files. Only 340 # used if log_rotate_max_bytes is greater than 0. 341 # Currently only supported on Windows. On other platforms, use an 342 # external tool such as 'logrotate' to manage log files. 343 "log_rotate_backup_count": int, 344 # If an event is above this size, it will be trimmed before putting it on the event bus 345 "max_event_size": int, 346 # Enable old style events to be sent on minion_startup. Change default to False in 3001 release 347 "enable_legacy_startup_events": bool, 348 # Always execute states with test=True if this flag is set 349 "test": bool, 350 # Tell the loader to attempt to import *.pyx cython files if cython is available 351 "cython_enable": bool, 352 # Whether or not to load grains for FQDNs 353 "enable_fqdns_grains": bool, 354 # Whether or not to load grains for the GPU 355 "enable_gpu_grains": bool, 356 # Tell the loader to attempt to import *.zip archives 357 "enable_zip_modules": bool, 358 # Tell the client to show minions that have timed out 359 "show_timeout": bool, 360 # Tell the client to display the jid when a job is published 361 "show_jid": bool, 362 # Ensure that a generated jid is always unique. If this is set, the jid 363 # format is different due to an underscore and process id being appended 364 # to the jid. WARNING: A change to the jid format may break external 365 # applications that depend on the original format. 366 "unique_jid": bool, 367 # Tells the highstate outputter to show successful states. False will omit successes. 368 "state_verbose": bool, 369 # Specify the format for state outputs. See highstate outputter for additional details. 370 "state_output": str, 371 # Tells the highstate outputter to only report diffs of states that changed 372 "state_output_diff": bool, 373 # Tells the highstate outputter whether profile information will be shown for each state run 374 "state_output_profile": bool, 375 # When true, states run in the order defined in an SLS file, unless requisites re-order them 376 "state_auto_order": bool, 377 # Fire events as state chunks are processed by the state compiler 378 "state_events": bool, 379 # The number of seconds a minion should wait before retry when attempting authentication 380 "acceptance_wait_time": float, 381 # The number of seconds a minion should wait before giving up during authentication 382 "acceptance_wait_time_max": float, 383 # Retry a connection attempt if the master rejects a minion's public key 384 "rejected_retry": bool, 385 # The interval in which a daemon's main loop should attempt to perform all necessary tasks 386 # for normal operation 387 "loop_interval": float, 388 # Perform pre-flight verification steps before daemon startup, such as checking configuration 389 # files and certain directories. 390 "verify_env": bool, 391 # The grains dictionary for a minion, containing specific "facts" about the minion 392 "grains": dict, 393 # Allow a daemon to function even if the key directories are not secured 394 "permissive_pki_access": bool, 395 # The passphrase of the master's private key 396 "key_pass": (type(None), str), 397 # The passphrase of the master's private signing key 398 "signing_key_pass": (type(None), str), 399 # The path to a directory to pull in configuration file includes 400 "default_include": str, 401 # If a minion is running an esky build of salt, upgrades can be performed using the url 402 # defined here. See saltutil.update() for additional information 403 "update_url": (bool, str), 404 # If using update_url with saltutil.update(), provide a list of services to be restarted 405 # post-install 406 "update_restart_services": list, 407 # The number of seconds to sleep between retrying an attempt to resolve the hostname of a 408 # salt master 409 "retry_dns": float, 410 "retry_dns_count": (type(None), int), 411 # In the case when the resolve of the salt master hostname fails, fall back to localhost 412 "resolve_dns_fallback": bool, 413 # set the zeromq_reconnect_ivl option on the minion. 414 # http://lists.zeromq.org/pipermail/zeromq-dev/2011-January/008845.html 415 "recon_max": float, 416 # If recon_randomize is set, this specifies the lower bound for the randomized period 417 "recon_default": float, 418 # Tells the minion to choose a bounded, random interval to have zeromq attempt to reconnect 419 # in the event of a disconnect event 420 "recon_randomize": bool, 421 # Configures retry interval, randomized between timer and timer_max if timer_max > 0 422 "return_retry_timer": int, 423 "return_retry_timer_max": int, 424 # Configures amount of return retries 425 "return_retry_tries": int, 426 # Specify one or more returners in which all events will be sent to. Requires that the returners 427 # in question have an event_return(event) function! 428 "event_return": (list, str), 429 # The number of events to queue up in memory before pushing them down the pipe to an event 430 # returner specified by 'event_return' 431 "event_return_queue": int, 432 # The number of seconds that events can languish in the queue before we flush them. 433 # The goal here is to ensure that if the bus is not busy enough to reach a total 434 # `event_return_queue` events won't get stale. 435 "event_return_queue_max_seconds": int, 436 # Only forward events to an event returner if it matches one of the tags in this list 437 "event_return_whitelist": list, 438 # Events matching a tag in this list should never be sent to an event returner. 439 "event_return_blacklist": list, 440 # default match type for filtering events tags: startswith, endswith, find, regex, fnmatch 441 "event_match_type": str, 442 # This pidfile to write out to when a daemon starts 443 "pidfile": str, 444 # Used with the SECO range master tops system 445 "range_server": str, 446 # The tcp keepalive interval to set on TCP ports. This setting can be used to tune Salt 447 # connectivity issues in messy network environments with misbehaving firewalls 448 "tcp_keepalive": bool, 449 # Sets zeromq TCP keepalive idle. May be used to tune issues with minion disconnects 450 "tcp_keepalive_idle": float, 451 # Sets zeromq TCP keepalive count. May be used to tune issues with minion disconnects 452 "tcp_keepalive_cnt": float, 453 # Sets zeromq TCP keepalive interval. May be used to tune issues with minion disconnects. 454 "tcp_keepalive_intvl": float, 455 # The network interface for a daemon to bind to 456 "interface": str, 457 # The port for a salt master to broadcast publications on. This will also be the port minions 458 # connect to to listen for publications. 459 "publish_port": int, 460 # TODO unknown option! 461 "auth_mode": int, 462 # listen queue size / backlog 463 "zmq_backlog": int, 464 # Set the zeromq high water mark on the publisher interface. 465 # http://api.zeromq.org/3-2:zmq-setsockopt 466 "pub_hwm": int, 467 # IPC buffer size 468 # Refs https://github.com/saltstack/salt/issues/34215 469 "ipc_write_buffer": int, 470 # various subprocess niceness levels 471 "req_server_niceness": (type(None), int), 472 "pub_server_niceness": (type(None), int), 473 "fileserver_update_niceness": (type(None), int), 474 "maintenance_niceness": (type(None), int), 475 "mworker_niceness": (type(None), int), 476 "mworker_queue_niceness": (type(None), int), 477 "event_return_niceness": (type(None), int), 478 "event_publisher_niceness": (type(None), int), 479 "reactor_niceness": (type(None), int), 480 # The number of MWorker processes for a master to startup. This number needs to scale up as 481 # the number of connected minions increases. 482 "worker_threads": int, 483 # The port for the master to listen to returns on. The minion needs to connect to this port 484 # to send returns. 485 "ret_port": int, 486 # The number of hours to keep jobs around in the job cache on the master 487 "keep_jobs": int, 488 # If the returner supports `clean_old_jobs`, then at cleanup time, 489 # archive the job data before deleting it. 490 "archive_jobs": bool, 491 # A master-only copy of the file_roots dictionary, used by the state compiler 492 "master_roots": dict, 493 # Add the proxymodule LazyLoader object to opts. This breaks many things 494 # but this was the default pre 2015.8.2. This should default to 495 # False in 2016.3.0 496 "add_proxymodule_to_opts": bool, 497 # Merge pillar data into configuration opts. 498 # As multiple proxies can run on the same server, we may need different 499 # configuration options for each, while there's one single configuration file. 500 # The solution is merging the pillar data of each proxy minion into the opts. 501 "proxy_merge_pillar_in_opts": bool, 502 # Deep merge of pillar data into configuration opts. 503 # Evaluated only when `proxy_merge_pillar_in_opts` is True. 504 "proxy_deep_merge_pillar_in_opts": bool, 505 # The strategy used when merging pillar into opts. 506 # Considered only when `proxy_merge_pillar_in_opts` is True. 507 "proxy_merge_pillar_in_opts_strategy": str, 508 # Allow enabling mine details using pillar data. 509 "proxy_mines_pillar": bool, 510 # In some particular cases, always alive proxies are not beneficial. 511 # This option can be used in those less dynamic environments: 512 # the user can request the connection 513 # always alive, or init-shutdown per command. 514 "proxy_always_alive": bool, 515 # Poll the connection state with the proxy minion 516 # If enabled, this option requires the function `alive` 517 # to be implemented in the proxy module 518 "proxy_keep_alive": bool, 519 # Frequency of the proxy_keep_alive, in minutes 520 "proxy_keep_alive_interval": int, 521 # Update intervals 522 "roots_update_interval": int, 523 "azurefs_update_interval": int, 524 "gitfs_update_interval": int, 525 "git_pillar_update_interval": int, 526 "hgfs_update_interval": int, 527 "minionfs_update_interval": int, 528 "s3fs_update_interval": int, 529 "svnfs_update_interval": int, 530 # NOTE: git_pillar_base, git_pillar_fallback, git_pillar_branch, 531 # git_pillar_env, and git_pillar_root omitted here because their values 532 # could conceivably be loaded as non-string types, which is OK because 533 # git_pillar will normalize them to strings. But rather than include all the 534 # possible types they could be, we'll just skip type-checking. 535 "git_pillar_ssl_verify": bool, 536 "git_pillar_global_lock": bool, 537 "git_pillar_user": str, 538 "git_pillar_password": str, 539 "git_pillar_insecure_auth": bool, 540 "git_pillar_privkey": str, 541 "git_pillar_pubkey": str, 542 "git_pillar_passphrase": str, 543 "git_pillar_refspecs": list, 544 "git_pillar_includes": bool, 545 "git_pillar_verify_config": bool, 546 # NOTE: gitfs_base, gitfs_fallback, gitfs_mountpoint, and gitfs_root omitted 547 # here because their values could conceivably be loaded as non-string types, 548 # which is OK because gitfs will normalize them to strings. But rather than 549 # include all the possible types they could be, we'll just skip type-checking. 550 "gitfs_remotes": list, 551 "gitfs_insecure_auth": bool, 552 "gitfs_privkey": str, 553 "gitfs_pubkey": str, 554 "gitfs_passphrase": str, 555 "gitfs_saltenv_whitelist": list, 556 "gitfs_saltenv_blacklist": list, 557 "gitfs_ssl_verify": bool, 558 "gitfs_global_lock": bool, 559 "gitfs_saltenv": list, 560 "gitfs_ref_types": list, 561 "gitfs_refspecs": list, 562 "gitfs_disable_saltenv_mapping": bool, 563 "hgfs_remotes": list, 564 "hgfs_mountpoint": str, 565 "hgfs_root": str, 566 "hgfs_base": str, 567 "hgfs_branch_method": str, 568 "hgfs_saltenv_whitelist": list, 569 "hgfs_saltenv_blacklist": list, 570 "svnfs_remotes": list, 571 "svnfs_mountpoint": str, 572 "svnfs_root": str, 573 "svnfs_trunk": str, 574 "svnfs_branches": str, 575 "svnfs_tags": str, 576 "svnfs_saltenv_whitelist": list, 577 "svnfs_saltenv_blacklist": list, 578 "minionfs_env": str, 579 "minionfs_mountpoint": str, 580 "minionfs_whitelist": list, 581 "minionfs_blacklist": list, 582 # Specify a list of external pillar systems to use 583 "ext_pillar": list, 584 # Reserved for future use to version the pillar structure 585 "pillar_version": int, 586 # Whether or not a copy of the master opts dict should be rendered into minion pillars 587 "pillar_opts": bool, 588 # Cache the master pillar to disk to avoid having to pass through the rendering system 589 "pillar_cache": bool, 590 # Pillar cache TTL, in seconds. Has no effect unless `pillar_cache` is True 591 "pillar_cache_ttl": int, 592 # Pillar cache backend. Defaults to `disk` which stores caches in the master cache 593 "pillar_cache_backend": str, 594 # Cache the GPG data to avoid having to pass through the gpg renderer 595 "gpg_cache": bool, 596 # GPG data cache TTL, in seconds. Has no effect unless `gpg_cache` is True 597 "gpg_cache_ttl": int, 598 # GPG data cache backend. Defaults to `disk` which stores caches in the master cache 599 "gpg_cache_backend": str, 600 "pillar_safe_render_error": bool, 601 # When creating a pillar, there are several strategies to choose from when 602 # encountering duplicate values 603 "pillar_source_merging_strategy": str, 604 # Recursively merge lists by aggregating them instead of replacing them. 605 "pillar_merge_lists": bool, 606 # If True, values from included pillar SLS targets will override 607 "pillar_includes_override_sls": bool, 608 # How to merge multiple top files from multiple salt environments 609 # (saltenvs); can be 'merge' or 'same' 610 "top_file_merging_strategy": str, 611 # The ordering for salt environment merging, when top_file_merging_strategy 612 # is set to 'same' 613 "env_order": list, 614 # The salt environment which provides the default top file when 615 # top_file_merging_strategy is set to 'same'; defaults to 'base' 616 "default_top": str, 617 "ping_on_rotate": bool, 618 "peer": dict, 619 "preserve_minion_cache": bool, 620 "syndic_master": (str, list), 621 # The behaviour of the multimaster syndic when connection to a master of masters failed. Can 622 # specify 'random' (default) or 'ordered'. If set to 'random' masters will be iterated in random 623 # order if 'ordered' the configured order will be used. 624 "syndic_failover": str, 625 "syndic_forward_all_events": bool, 626 "runner_dirs": list, 627 "client_acl_verify": bool, 628 "publisher_acl": dict, 629 "publisher_acl_blacklist": dict, 630 "sudo_acl": bool, 631 "external_auth": dict, 632 "token_expire": int, 633 "token_expire_user_override": (bool, dict), 634 "file_recv": bool, 635 "file_recv_max_size": int, 636 "file_ignore_regex": (list, str), 637 "file_ignore_glob": (list, str), 638 "fileserver_backend": list, 639 "fileserver_followsymlinks": bool, 640 "fileserver_ignoresymlinks": bool, 641 "fileserver_limit_traversal": bool, 642 "fileserver_verify_config": bool, 643 # Optionally apply '*' permissioins to any user. By default '*' is a fallback case that is 644 # applied only if the user didn't matched by other matchers. 645 "permissive_acl": bool, 646 # Optionally enables keeping the calculated user's auth list in the token file. 647 "keep_acl_in_token": bool, 648 # Auth subsystem module to use to get authorized access list for a user. By default it's the 649 # same module used for external authentication. 650 "eauth_acl_module": str, 651 # Subsystem to use to maintain eauth tokens. By default, tokens are stored on the local 652 # filesystem 653 "eauth_tokens": str, 654 # The number of open files a daemon is allowed to have open. Frequently needs to be increased 655 # higher than the system default in order to account for the way zeromq consumes file handles. 656 "max_open_files": int, 657 # Automatically accept any key provided to the master. Implies that the key will be preserved 658 # so that subsequent connections will be authenticated even if this option has later been 659 # turned off. 660 "auto_accept": bool, 661 "autosign_timeout": int, 662 # A mapping of external systems that can be used to generate topfile data. 663 "master_tops": dict, 664 # Whether or not matches from master_tops should be executed before or 665 # after those from the top file(s). 666 "master_tops_first": bool, 667 # A flag that should be set on a top-level master when it is ordering around subordinate masters 668 # via the use of a salt syndic 669 "order_masters": bool, 670 # Whether or not to cache jobs so that they can be examined later on 671 "job_cache": bool, 672 # Define a returner to be used as an external job caching storage backend 673 "ext_job_cache": str, 674 # Specify a returner for the master to use as a backend storage system to cache jobs returns 675 # that it receives 676 "master_job_cache": str, 677 # Specify whether the master should store end times for jobs as returns come in 678 "job_cache_store_endtime": bool, 679 # The minion data cache is a cache of information about the minions stored on the master. 680 # This information is primarily the pillar and grains data. The data is cached in the master 681 # cachedir under the name of the minion and used to predetermine what minions are expected to 682 # reply from executions. 683 "minion_data_cache": bool, 684 # The number of seconds between AES key rotations on the master 685 "publish_session": int, 686 # Defines a salt reactor. See https://docs.saltproject.io/en/latest/topics/reactor/ 687 "reactor": list, 688 # The TTL for the cache of the reactor configuration 689 "reactor_refresh_interval": int, 690 # The number of workers for the runner/wheel in the reactor 691 "reactor_worker_threads": int, 692 # The queue size for workers in the reactor 693 "reactor_worker_hwm": int, 694 # Defines engines. See https://docs.saltproject.io/en/latest/topics/engines/ 695 "engines": list, 696 # Whether or not to store runner returns in the job cache 697 "runner_returns": bool, 698 "serial": str, 699 "search": str, 700 # A compound target definition. 701 # See: https://docs.saltproject.io/en/latest/topics/targeting/nodegroups.html 702 "nodegroups": (dict, list), 703 # List-only nodegroups for salt-ssh. Each group must be formed as either a 704 # comma-separated list, or a YAML list. 705 "ssh_list_nodegroups": dict, 706 # By default, salt-ssh uses its own specially-generated RSA key to auth 707 # against minions. If this is set to True, salt-ssh will look in 708 # for a key at ~/.ssh/id_rsa, and fall back to using its own specially- 709 # generated RSA key if that file doesn't exist. 710 "ssh_use_home_key": bool, 711 # The logfile location for salt-key 712 "key_logfile": str, 713 # The upper bound for the random number of seconds that a minion should 714 # delay when starting in up before it connects to a master. This can be 715 # used to mitigate a thundering-herd scenario when many minions start up 716 # at once and attempt to all connect immediately to the master 717 "random_startup_delay": int, 718 # The source location for the winrepo sls files 719 # (used by win_pkg.py, minion only) 720 "winrepo_source_dir": str, 721 "winrepo_dir": str, 722 "winrepo_dir_ng": str, 723 "winrepo_cachefile": str, 724 # NOTE: winrepo_branch omitted here because its value could conceivably be 725 # loaded as a non-string type, which is OK because winrepo will normalize 726 # them to strings. But rather than include all the possible types it could 727 # be, we'll just skip type-checking. 728 "winrepo_cache_expire_max": int, 729 "winrepo_cache_expire_min": int, 730 "winrepo_remotes": list, 731 "winrepo_remotes_ng": list, 732 "winrepo_ssl_verify": bool, 733 "winrepo_user": str, 734 "winrepo_password": str, 735 "winrepo_insecure_auth": bool, 736 "winrepo_privkey": str, 737 "winrepo_pubkey": str, 738 "winrepo_passphrase": str, 739 "winrepo_refspecs": list, 740 # Set a hard limit for the amount of memory modules can consume on a minion. 741 "modules_max_memory": int, 742 # Blacklist specific core grains to be filtered 743 "grains_blacklist": list, 744 # The number of minutes between the minion refreshing its cache of grains 745 "grains_refresh_every": int, 746 # Use lspci to gather system data for grains on a minion 747 "enable_lspci": bool, 748 # The number of seconds for the salt client to wait for additional syndics to 749 # check in with their lists of expected minions before giving up 750 "syndic_wait": int, 751 # Override Jinja environment option defaults for all templates except sls templates 752 "jinja_env": dict, 753 # Set Jinja environment options for sls templates 754 "jinja_sls_env": dict, 755 # If this is set to True leading spaces and tabs are stripped from the start 756 # of a line to a block. 757 "jinja_lstrip_blocks": bool, 758 # If this is set to True the first newline after a Jinja block is removed 759 "jinja_trim_blocks": bool, 760 # Cache minion ID to file 761 "minion_id_caching": bool, 762 # Always generate minion id in lowercase. 763 "minion_id_lowercase": bool, 764 # Remove either a single domain (foo.org), or all (True) from a generated minion id. 765 "minion_id_remove_domain": (str, bool), 766 # If set, the master will sign all publications before they are sent out 767 "sign_pub_messages": bool, 768 # The size of key that should be generated when creating new keys 769 "keysize": int, 770 # The transport system for this daemon. (i.e. zeromq, tcp, detect, etc) 771 "transport": str, 772 # The number of seconds to wait when the client is requesting information about running jobs 773 "gather_job_timeout": int, 774 # The number of seconds to wait before timing out an authentication request 775 "auth_timeout": int, 776 # The number of attempts to authenticate to a master before giving up 777 "auth_tries": int, 778 # The number of attempts to connect to a master before giving up. 779 # Set this to -1 for unlimited attempts. This allows for a master to have 780 # downtime and the minion to reconnect to it later when it comes back up. 781 # In 'failover' mode, it is the number of attempts for each set of masters. 782 # In this mode, it will cycle through the list of masters for each attempt. 783 "master_tries": int, 784 # Never give up when trying to authenticate to a master 785 "auth_safemode": bool, 786 # Selects a random master when starting a minion up in multi-master mode or 787 # when starting a minion with salt-call. ``master`` must be a list. 788 "random_master": bool, 789 # An upper bound for the amount of time for a minion to sleep before attempting to 790 # reauth after a restart. 791 "random_reauth_delay": int, 792 # The number of seconds for a syndic to poll for new messages that need to be forwarded 793 "syndic_event_forward_timeout": float, 794 # The length that the syndic event queue must hit before events are popped off and forwarded 795 "syndic_jid_forward_cache_hwm": int, 796 # Salt SSH configuration 797 "ssh_passwd": str, 798 "ssh_port": str, 799 "ssh_sudo": bool, 800 "ssh_sudo_user": str, 801 "ssh_timeout": float, 802 "ssh_user": str, 803 "ssh_scan_ports": str, 804 "ssh_scan_timeout": float, 805 "ssh_identities_only": bool, 806 "ssh_log_file": str, 807 "ssh_config_file": str, 808 "ssh_merge_pillar": bool, 809 "ssh_run_pre_flight": bool, 810 "cluster_mode": bool, 811 "sqlite_queue_dir": str, 812 "queue_dirs": list, 813 # Instructs the minion to ping its master(s) every n number of minutes. Used 814 # primarily as a mitigation technique against minion disconnects. 815 "ping_interval": int, 816 # Instructs the salt CLI to print a summary of a minion responses before returning 817 "cli_summary": bool, 818 # The maximum number of minion connections allowed by the master. Can have performance 819 # implications in large setups. 820 "max_minions": int, 821 "username": (type(None), str), 822 "password": (type(None), str), 823 # Use zmq.SUSCRIBE to limit listening sockets to only process messages bound for them 824 "zmq_filtering": bool, 825 # Connection caching. Can greatly speed up salt performance. 826 "con_cache": bool, 827 "rotate_aes_key": bool, 828 # Cache ZeroMQ connections. Can greatly improve salt performance. 829 "cache_sreqs": bool, 830 # Can be set to override the python_shell=False default in the cmd module 831 "cmd_safe": bool, 832 # Used by salt-api for master requests timeout 833 "rest_timeout": int, 834 # If set, all minion exec module actions will be rerouted through sudo as this user 835 "sudo_user": str, 836 # HTTP connection timeout in seconds. Applied for tornado http fetch functions like cp.get_url 837 # should be greater than overall download time 838 "http_connect_timeout": float, 839 # HTTP request timeout in seconds. Applied for tornado http fetch functions like cp.get_url 840 # should be greater than overall download time 841 "http_request_timeout": float, 842 # HTTP request max file content size. 843 "http_max_body": int, 844 # Delay in seconds before executing bootstrap (Salt Cloud) 845 "bootstrap_delay": int, 846 # If a proxymodule has a function called 'grains', then call it during 847 # regular grains loading and merge the results with the proxy's grains 848 # dictionary. Otherwise it is assumed that the module calls the grains 849 # function in a custom way and returns the data elsewhere 850 # 851 # Default to False for 2016.3 and 2016.11. Switch to True for 2017.7.0 852 "proxy_merge_grains_in_module": bool, 853 # Command to use to restart salt-minion 854 "minion_restart_command": list, 855 # Whether or not a minion should send the results of a command back to the master 856 # Useful when a returner is the source of truth for a job result 857 "pub_ret": bool, 858 # HTTP proxy settings. Used in tornado fetch functions, apt-key etc 859 "proxy_host": str, 860 "proxy_username": str, 861 "proxy_password": str, 862 "proxy_port": int, 863 # Exclude list of hostnames from proxy 864 "no_proxy": list, 865 # Minion de-dup jid cache max size 866 "minion_jid_queue_hwm": int, 867 # Minion data cache driver (one of satl.cache.* modules) 868 "cache": str, 869 # Enables a fast in-memory cache booster and sets the expiration time. 870 "memcache_expire_seconds": int, 871 # Set a memcache limit in items (bank + key) per cache storage (driver + driver_opts). 872 "memcache_max_items": int, 873 # Each time a cache storage got full cleanup all the expired items not just the oldest one. 874 "memcache_full_cleanup": bool, 875 # Enable collecting the memcache stats and log it on `debug` log level. 876 "memcache_debug": bool, 877 # Thin and minimal Salt extra modules 878 "thin_extra_mods": str, 879 "min_extra_mods": str, 880 # Default returners minion should use. List or comma-delimited string 881 "return": (str, list), 882 # TLS/SSL connection options. This could be set to a dictionary containing arguments 883 # corresponding to python ssl.wrap_socket method. For details see: 884 # http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer 885 # http://docs.python.org/2/library/ssl.html#ssl.wrap_socket 886 # Note: to set enum arguments values like `cert_reqs` and `ssl_version` use constant names 887 # without ssl module prefix: `CERT_REQUIRED` or `PROTOCOL_SSLv23`. 888 "ssl": (dict, bool, type(None)), 889 # Controls how a multi-function job returns its data. If this is False, 890 # it will return its data using a dictionary with the function name as 891 # the key. This is compatible with legacy systems. If this is True, it 892 # will return its data using an array in the same order as the input 893 # array of functions to execute. This allows for calling the same 894 # function multiple times in the same multi-function job. 895 "multifunc_ordered": bool, 896 # Controls whether beacons are set up before a connection 897 # to the master is attempted. 898 "beacons_before_connect": bool, 899 # Controls whether the scheduler is set up before a connection 900 # to the master is attempted. 901 "scheduler_before_connect": bool, 902 # Whitelist/blacklist specific modules to be synced 903 "extmod_whitelist": dict, 904 "extmod_blacklist": dict, 905 # django auth 906 "django_auth_path": str, 907 "django_auth_settings": str, 908 # Number of times to try to auth with the master on a reconnect with the 909 # tcp transport 910 "tcp_authentication_retries": int, 911 # Backoff interval in seconds for minion reconnect with tcp transport 912 "tcp_reconnect_backoff": float, 913 # Permit or deny allowing minions to request revoke of its own key 914 "allow_minion_key_revoke": bool, 915 # File chunk size for salt-cp 916 "salt_cp_chunk_size": int, 917 # Require that the minion sign messages it posts to the master on the event 918 # bus 919 "minion_sign_messages": bool, 920 # Have master drop messages from minions for which their signatures do 921 # not verify 922 "drop_messages_signature_fail": bool, 923 # Require that payloads from minions have a 'sig' entry 924 # (in other words, require that minions have 'minion_sign_messages' 925 # turned on) 926 "require_minion_sign_messages": bool, 927 # The list of config entries to be passed to external pillar function as 928 # part of the extra_minion_data param 929 # Subconfig entries can be specified by using the ':' notation (e.g. key:subkey) 930 "pass_to_ext_pillars": (str, list), 931 # SSDP discovery publisher description. 932 # Contains publisher configuration and minion mapping. 933 # Setting it to False disables discovery 934 "discovery": (dict, bool), 935 # Scheduler should be a dictionary 936 "schedule": dict, 937 # Whether to fire auth events 938 "auth_events": bool, 939 # Whether to fire Minion data cache refresh events 940 "minion_data_cache_events": bool, 941 # Enable calling ssh minions from the salt master 942 "enable_ssh_minions": bool, 943 # Thorium saltenv 944 "thoriumenv": (type(None), str), 945 # Thorium top file location 946 "thorium_top": str, 947 # Allow raw_shell option when using the ssh 948 # client via the Salt API 949 "netapi_allow_raw_shell": bool, 950 "disabled_requisites": (str, list), 951 # Feature flag config 952 "features": dict, 953 "fips_mode": bool, 954 # Feature flag to enable checking if master is connected to a host 955 # on a given port 956 "detect_remote_minions": bool, 957 # The port to be used when checking if a master is connected to a 958 # minion 959 "remote_minions_port": int, 960 } 961) 962 963# default configurations 964DEFAULT_MINION_OPTS = immutabletypes.freeze( 965 { 966 "interface": "0.0.0.0", 967 "master": "salt", 968 "master_type": "str", 969 "master_uri_format": "default", 970 "source_interface_name": "", 971 "source_address": "", 972 "source_ret_port": 0, 973 "source_publish_port": 0, 974 "master_port": 4506, 975 "master_finger": "", 976 "master_shuffle": False, 977 "master_alive_interval": 0, 978 "master_failback": False, 979 "master_failback_interval": 0, 980 "verify_master_pubkey_sign": False, 981 "sign_pub_messages": False, 982 "always_verify_signature": False, 983 "master_sign_key_name": "master_sign", 984 "syndic_finger": "", 985 "user": salt.utils.user.get_user(), 986 "root_dir": salt.syspaths.ROOT_DIR, 987 "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "minion"), 988 "id": "", 989 "id_function": {}, 990 "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "minion"), 991 "append_minionid_config_dirs": [], 992 "cache_jobs": False, 993 "grains_blacklist": [], 994 "grains_cache": False, 995 "grains_cache_expiration": 300, 996 "grains_deep_merge": False, 997 "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "minion"), 998 "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "minion"), 999 "sock_pool_size": 1, 1000 "backup_mode": "", 1001 "renderer": "jinja|yaml", 1002 "renderer_whitelist": [], 1003 "renderer_blacklist": [], 1004 "random_startup_delay": 0, 1005 "failhard": False, 1006 "autoload_dynamic_modules": True, 1007 "saltenv": None, 1008 "lock_saltenv": False, 1009 "pillarenv": None, 1010 "pillarenv_from_saltenv": False, 1011 "pillar_opts": False, 1012 "pillar_source_merging_strategy": "smart", 1013 "pillar_merge_lists": False, 1014 "pillar_includes_override_sls": False, 1015 # ``pillar_cache``, ``pillar_cache_ttl``, ``pillar_cache_backend``, 1016 # ``gpg_cache``, ``gpg_cache_ttl`` and ``gpg_cache_backend`` 1017 # are not used on the minion but are unavoidably in the code path 1018 "pillar_cache": False, 1019 "pillar_cache_ttl": 3600, 1020 "pillar_cache_backend": "disk", 1021 "gpg_cache": False, 1022 "gpg_cache_ttl": 86400, 1023 "gpg_cache_backend": "disk", 1024 "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "minion", "extmods"), 1025 "state_top": "top.sls", 1026 "state_top_saltenv": None, 1027 "startup_states": "", 1028 "sls_list": [], 1029 "start_event_grains": [], 1030 "top_file": "", 1031 "thoriumenv": None, 1032 "thorium_top": "top.sls", 1033 "thorium_interval": 0.5, 1034 "thorium_roots": {"base": [salt.syspaths.BASE_THORIUM_ROOTS_DIR]}, 1035 "file_client": "remote", 1036 "local": False, 1037 "use_master_when_local": False, 1038 "file_roots": { 1039 "base": [salt.syspaths.BASE_FILE_ROOTS_DIR, salt.syspaths.SPM_FORMULA_PATH] 1040 }, 1041 "top_file_merging_strategy": "merge", 1042 "env_order": [], 1043 "default_top": "base", 1044 "fileserver_limit_traversal": False, 1045 "file_recv": False, 1046 "file_recv_max_size": 100, 1047 "file_ignore_regex": [], 1048 "file_ignore_glob": [], 1049 "fileserver_backend": ["roots"], 1050 "fileserver_followsymlinks": True, 1051 "fileserver_ignoresymlinks": False, 1052 "pillar_roots": { 1053 "base": [salt.syspaths.BASE_PILLAR_ROOTS_DIR, salt.syspaths.SPM_PILLAR_PATH] 1054 }, 1055 "on_demand_ext_pillar": ["libvirt", "virtkey"], 1056 "decrypt_pillar": [], 1057 "decrypt_pillar_delimiter": ":", 1058 "decrypt_pillar_default": "gpg", 1059 "decrypt_pillar_renderers": ["gpg"], 1060 # Update intervals 1061 "roots_update_interval": DEFAULT_INTERVAL, 1062 "azurefs_update_interval": DEFAULT_INTERVAL, 1063 "gitfs_update_interval": DEFAULT_INTERVAL, 1064 "git_pillar_update_interval": DEFAULT_INTERVAL, 1065 "hgfs_update_interval": DEFAULT_INTERVAL, 1066 "minionfs_update_interval": DEFAULT_INTERVAL, 1067 "s3fs_update_interval": DEFAULT_INTERVAL, 1068 "svnfs_update_interval": DEFAULT_INTERVAL, 1069 "git_pillar_base": "master", 1070 "git_pillar_branch": "master", 1071 "git_pillar_env": "", 1072 "git_pillar_fallback": "", 1073 "git_pillar_root": "", 1074 "git_pillar_ssl_verify": True, 1075 "git_pillar_global_lock": True, 1076 "git_pillar_user": "", 1077 "git_pillar_password": "", 1078 "git_pillar_insecure_auth": False, 1079 "git_pillar_privkey": "", 1080 "git_pillar_pubkey": "", 1081 "git_pillar_passphrase": "", 1082 "git_pillar_refspecs": _DFLT_REFSPECS, 1083 "git_pillar_includes": True, 1084 "gitfs_remotes": [], 1085 "gitfs_mountpoint": "", 1086 "gitfs_root": "", 1087 "gitfs_base": "master", 1088 "gitfs_fallback": "", 1089 "gitfs_user": "", 1090 "gitfs_password": "", 1091 "gitfs_insecure_auth": False, 1092 "gitfs_privkey": "", 1093 "gitfs_pubkey": "", 1094 "gitfs_passphrase": "", 1095 "gitfs_saltenv_whitelist": [], 1096 "gitfs_saltenv_blacklist": [], 1097 "gitfs_global_lock": True, 1098 "gitfs_ssl_verify": True, 1099 "gitfs_saltenv": [], 1100 "gitfs_ref_types": ["branch", "tag", "sha"], 1101 "gitfs_refspecs": _DFLT_REFSPECS, 1102 "gitfs_disable_saltenv_mapping": False, 1103 "unique_jid": False, 1104 "hash_type": "sha256", 1105 "optimization_order": [0, 1, 2], 1106 "disable_modules": [], 1107 "disable_returners": [], 1108 "whitelist_modules": [], 1109 "module_dirs": [], 1110 "returner_dirs": [], 1111 "grains_dirs": [], 1112 "states_dirs": [], 1113 "render_dirs": [], 1114 "outputter_dirs": [], 1115 "utils_dirs": [], 1116 "publisher_acl": {}, 1117 "publisher_acl_blacklist": {}, 1118 "providers": {}, 1119 "clean_dynamic_modules": True, 1120 "open_mode": False, 1121 "auto_accept": True, 1122 "autosign_timeout": 120, 1123 "multiprocessing": True, 1124 "process_count_max": -1, 1125 "mine_enabled": True, 1126 "mine_return_job": False, 1127 "mine_interval": 60, 1128 "ipc_mode": _DFLT_IPC_MODE, 1129 "ipc_write_buffer": _DFLT_IPC_WBUFFER, 1130 "ipv6": None, 1131 "file_buffer_size": 262144, 1132 "tcp_pub_port": 4510, 1133 "tcp_pull_port": 4511, 1134 "tcp_authentication_retries": 5, 1135 "tcp_reconnect_backoff": 1, 1136 "log_file": os.path.join(salt.syspaths.LOGS_DIR, "minion"), 1137 "log_level": "warning", 1138 "log_level_logfile": None, 1139 "log_datefmt": _DFLT_LOG_DATEFMT, 1140 "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, 1141 "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, 1142 "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, 1143 "log_fmt_jid": _DFLT_LOG_FMT_JID, 1144 "log_granular_levels": {}, 1145 "log_rotate_max_bytes": 0, 1146 "log_rotate_backup_count": 0, 1147 "max_event_size": 1048576, 1148 "enable_legacy_startup_events": True, 1149 "test": False, 1150 "ext_job_cache": "", 1151 "cython_enable": False, 1152 "enable_fqdns_grains": _DFLT_FQDNS_GRAINS, 1153 "enable_gpu_grains": True, 1154 "enable_zip_modules": False, 1155 "state_verbose": True, 1156 "state_output": "full", 1157 "state_output_diff": False, 1158 "state_output_profile": True, 1159 "state_auto_order": True, 1160 "state_events": False, 1161 "state_aggregate": False, 1162 "snapper_states": False, 1163 "snapper_states_config": "root", 1164 "acceptance_wait_time": 10, 1165 "acceptance_wait_time_max": 0, 1166 "rejected_retry": False, 1167 "loop_interval": 1, 1168 "verify_env": True, 1169 "grains": {}, 1170 "permissive_pki_access": False, 1171 "default_include": "minion.d/*.conf", 1172 "update_url": False, 1173 "update_restart_services": [], 1174 "retry_dns": 30, 1175 "retry_dns_count": None, 1176 "resolve_dns_fallback": True, 1177 "recon_max": 10000, 1178 "recon_default": 1000, 1179 "recon_randomize": True, 1180 "return_retry_timer": 5, 1181 "return_retry_timer_max": 10, 1182 "return_retry_tries": 3, 1183 "random_reauth_delay": 10, 1184 "winrepo_source_dir": "salt://win/repo-ng/", 1185 "winrepo_dir": os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo"), 1186 "winrepo_dir_ng": os.path.join( 1187 salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo-ng" 1188 ), 1189 "winrepo_cachefile": "winrepo.p", 1190 "winrepo_cache_expire_max": 604800, 1191 "winrepo_cache_expire_min": 1800, 1192 "winrepo_remotes": ["https://github.com/saltstack/salt-winrepo.git"], 1193 "winrepo_remotes_ng": ["https://github.com/saltstack/salt-winrepo-ng.git"], 1194 "winrepo_branch": "master", 1195 "winrepo_fallback": "", 1196 "winrepo_ssl_verify": True, 1197 "winrepo_user": "", 1198 "winrepo_password": "", 1199 "winrepo_insecure_auth": False, 1200 "winrepo_privkey": "", 1201 "winrepo_pubkey": "", 1202 "winrepo_passphrase": "", 1203 "winrepo_refspecs": _DFLT_REFSPECS, 1204 "pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-minion.pid"), 1205 "range_server": "range:80", 1206 "reactor_refresh_interval": 60, 1207 "reactor_worker_threads": 10, 1208 "reactor_worker_hwm": 10000, 1209 "engines": [], 1210 "tcp_keepalive": True, 1211 "tcp_keepalive_idle": 300, 1212 "tcp_keepalive_cnt": -1, 1213 "tcp_keepalive_intvl": -1, 1214 "modules_max_memory": -1, 1215 "grains_refresh_every": 0, 1216 "minion_id_caching": True, 1217 "minion_id_lowercase": False, 1218 "minion_id_remove_domain": False, 1219 "keysize": 2048, 1220 "transport": "zeromq", 1221 "auth_timeout": 5, 1222 "auth_tries": 7, 1223 "master_tries": _MASTER_TRIES, 1224 "master_tops_first": False, 1225 "auth_safemode": False, 1226 "random_master": False, 1227 "cluster_mode": False, 1228 "restart_on_error": False, 1229 "ping_interval": 0, 1230 "username": None, 1231 "password": None, 1232 "zmq_filtering": False, 1233 "zmq_monitor": False, 1234 "cache_sreqs": True, 1235 "cmd_safe": True, 1236 "sudo_user": "", 1237 "http_connect_timeout": 20.0, # tornado default - 20 seconds 1238 "http_request_timeout": 1 * 60 * 60.0, # 1 hour 1239 "http_max_body": 100 * 1024 * 1024 * 1024, # 100GB 1240 "event_match_type": "startswith", 1241 "minion_restart_command": [], 1242 "pub_ret": True, 1243 "proxy_host": "", 1244 "proxy_username": "", 1245 "proxy_password": "", 1246 "proxy_port": 0, 1247 "minion_jid_queue_hwm": 100, 1248 "ssl": None, 1249 "multifunc_ordered": False, 1250 "beacons_before_connect": False, 1251 "scheduler_before_connect": False, 1252 "cache": "localfs", 1253 "salt_cp_chunk_size": 65536, 1254 "extmod_whitelist": {}, 1255 "extmod_blacklist": {}, 1256 "minion_sign_messages": False, 1257 "discovery": False, 1258 "schedule": {}, 1259 "ssh_merge_pillar": True, 1260 "disabled_requisites": [], 1261 "reactor_niceness": None, 1262 "fips_mode": False, 1263 } 1264) 1265 1266DEFAULT_MASTER_OPTS = immutabletypes.freeze( 1267 { 1268 "interface": "0.0.0.0", 1269 "publish_port": 4505, 1270 "zmq_backlog": 1000, 1271 "pub_hwm": 1000, 1272 "auth_mode": 1, 1273 "user": _MASTER_USER, 1274 "worker_threads": 5, 1275 "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "master"), 1276 "sock_pool_size": 1, 1277 "ret_port": 4506, 1278 "timeout": 5, 1279 "keep_jobs": 24, 1280 "archive_jobs": False, 1281 "root_dir": salt.syspaths.ROOT_DIR, 1282 "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "master"), 1283 "key_cache": "", 1284 "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "master"), 1285 "file_roots": { 1286 "base": [salt.syspaths.BASE_FILE_ROOTS_DIR, salt.syspaths.SPM_FORMULA_PATH] 1287 }, 1288 "master_roots": {"base": [salt.syspaths.BASE_MASTER_ROOTS_DIR]}, 1289 "pillar_roots": { 1290 "base": [salt.syspaths.BASE_PILLAR_ROOTS_DIR, salt.syspaths.SPM_PILLAR_PATH] 1291 }, 1292 "on_demand_ext_pillar": ["libvirt", "virtkey"], 1293 "decrypt_pillar": [], 1294 "decrypt_pillar_delimiter": ":", 1295 "decrypt_pillar_default": "gpg", 1296 "decrypt_pillar_renderers": ["gpg"], 1297 "thoriumenv": None, 1298 "thorium_top": "top.sls", 1299 "thorium_interval": 0.5, 1300 "thorium_roots": {"base": [salt.syspaths.BASE_THORIUM_ROOTS_DIR]}, 1301 "top_file_merging_strategy": "merge", 1302 "env_order": [], 1303 "saltenv": None, 1304 "lock_saltenv": False, 1305 "pillarenv": None, 1306 "default_top": "base", 1307 "file_client": "local", 1308 "local": True, 1309 # Update intervals 1310 "roots_update_interval": DEFAULT_INTERVAL, 1311 "azurefs_update_interval": DEFAULT_INTERVAL, 1312 "gitfs_update_interval": DEFAULT_INTERVAL, 1313 "git_pillar_update_interval": DEFAULT_INTERVAL, 1314 "hgfs_update_interval": DEFAULT_INTERVAL, 1315 "minionfs_update_interval": DEFAULT_INTERVAL, 1316 "s3fs_update_interval": DEFAULT_INTERVAL, 1317 "svnfs_update_interval": DEFAULT_INTERVAL, 1318 "git_pillar_base": "master", 1319 "git_pillar_branch": "master", 1320 "git_pillar_env": "", 1321 "git_pillar_fallback": "", 1322 "git_pillar_root": "", 1323 "git_pillar_ssl_verify": True, 1324 "git_pillar_global_lock": True, 1325 "git_pillar_user": "", 1326 "git_pillar_password": "", 1327 "git_pillar_insecure_auth": False, 1328 "git_pillar_privkey": "", 1329 "git_pillar_pubkey": "", 1330 "git_pillar_passphrase": "", 1331 "git_pillar_refspecs": _DFLT_REFSPECS, 1332 "git_pillar_includes": True, 1333 "git_pillar_verify_config": True, 1334 "gitfs_remotes": [], 1335 "gitfs_mountpoint": "", 1336 "gitfs_root": "", 1337 "gitfs_base": "master", 1338 "gitfs_fallback": "", 1339 "gitfs_user": "", 1340 "gitfs_password": "", 1341 "gitfs_insecure_auth": False, 1342 "gitfs_privkey": "", 1343 "gitfs_pubkey": "", 1344 "gitfs_passphrase": "", 1345 "gitfs_saltenv_whitelist": [], 1346 "gitfs_saltenv_blacklist": [], 1347 "gitfs_global_lock": True, 1348 "gitfs_ssl_verify": True, 1349 "gitfs_saltenv": [], 1350 "gitfs_ref_types": ["branch", "tag", "sha"], 1351 "gitfs_refspecs": _DFLT_REFSPECS, 1352 "gitfs_disable_saltenv_mapping": False, 1353 "hgfs_remotes": [], 1354 "hgfs_mountpoint": "", 1355 "hgfs_root": "", 1356 "hgfs_base": "default", 1357 "hgfs_branch_method": "branches", 1358 "hgfs_saltenv_whitelist": [], 1359 "hgfs_saltenv_blacklist": [], 1360 "show_timeout": True, 1361 "show_jid": False, 1362 "unique_jid": False, 1363 "svnfs_remotes": [], 1364 "svnfs_mountpoint": "", 1365 "svnfs_root": "", 1366 "svnfs_trunk": "trunk", 1367 "svnfs_branches": "branches", 1368 "svnfs_tags": "tags", 1369 "svnfs_saltenv_whitelist": [], 1370 "svnfs_saltenv_blacklist": [], 1371 "max_event_size": 1048576, 1372 "master_stats": False, 1373 "master_stats_event_iter": 60, 1374 "minionfs_env": "base", 1375 "minionfs_mountpoint": "", 1376 "minionfs_whitelist": [], 1377 "minionfs_blacklist": [], 1378 "ext_pillar": [], 1379 "pillar_version": 2, 1380 "pillar_opts": False, 1381 "pillar_safe_render_error": True, 1382 "pillar_source_merging_strategy": "smart", 1383 "pillar_merge_lists": False, 1384 "pillar_includes_override_sls": False, 1385 "pillar_cache": False, 1386 "pillar_cache_ttl": 3600, 1387 "pillar_cache_backend": "disk", 1388 "gpg_cache": False, 1389 "gpg_cache_ttl": 86400, 1390 "gpg_cache_backend": "disk", 1391 "ping_on_rotate": False, 1392 "peer": {}, 1393 "preserve_minion_cache": False, 1394 "syndic_master": "masterofmasters", 1395 "syndic_failover": "random", 1396 "syndic_forward_all_events": False, 1397 "syndic_log_file": os.path.join(salt.syspaths.LOGS_DIR, "syndic"), 1398 "syndic_pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-syndic.pid"), 1399 "outputter_dirs": [], 1400 "runner_dirs": [], 1401 "utils_dirs": [], 1402 "client_acl_verify": True, 1403 "publisher_acl": {}, 1404 "publisher_acl_blacklist": {}, 1405 "sudo_acl": False, 1406 "external_auth": {}, 1407 "token_expire": 43200, 1408 "token_expire_user_override": False, 1409 "permissive_acl": False, 1410 "keep_acl_in_token": False, 1411 "eauth_acl_module": "", 1412 "eauth_tokens": "localfs", 1413 "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "master", "extmods"), 1414 "module_dirs": [], 1415 "file_recv": False, 1416 "file_recv_max_size": 100, 1417 "file_buffer_size": 1048576, 1418 "file_ignore_regex": [], 1419 "file_ignore_glob": [], 1420 "fileserver_backend": ["roots"], 1421 "fileserver_followsymlinks": True, 1422 "fileserver_ignoresymlinks": False, 1423 "fileserver_limit_traversal": False, 1424 "fileserver_verify_config": True, 1425 "max_open_files": 100000, 1426 "hash_type": "sha256", 1427 "optimization_order": [0, 1, 2], 1428 "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "master"), 1429 "open_mode": False, 1430 "auto_accept": False, 1431 "renderer": "jinja|yaml", 1432 "renderer_whitelist": [], 1433 "renderer_blacklist": [], 1434 "failhard": False, 1435 "state_top": "top.sls", 1436 "state_top_saltenv": None, 1437 "master_tops": {}, 1438 "master_tops_first": False, 1439 "order_masters": False, 1440 "job_cache": True, 1441 "ext_job_cache": "", 1442 "master_job_cache": "local_cache", 1443 "job_cache_store_endtime": False, 1444 "minion_data_cache": True, 1445 "enforce_mine_cache": False, 1446 "ipc_mode": _DFLT_IPC_MODE, 1447 "ipc_write_buffer": _DFLT_IPC_WBUFFER, 1448 # various subprocess niceness levels 1449 "req_server_niceness": None, 1450 "pub_server_niceness": None, 1451 "fileserver_update_niceness": None, 1452 "mworker_niceness": None, 1453 "mworker_queue_niceness": None, 1454 "maintenance_niceness": None, 1455 "event_return_niceness": None, 1456 "event_publisher_niceness": None, 1457 "reactor_niceness": None, 1458 "ipv6": None, 1459 "tcp_master_pub_port": 4512, 1460 "tcp_master_pull_port": 4513, 1461 "tcp_master_publish_pull": 4514, 1462 "tcp_master_workers": 4515, 1463 "log_file": os.path.join(salt.syspaths.LOGS_DIR, "master"), 1464 "log_level": "warning", 1465 "log_level_logfile": None, 1466 "log_datefmt": _DFLT_LOG_DATEFMT, 1467 "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, 1468 "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, 1469 "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, 1470 "log_fmt_jid": _DFLT_LOG_FMT_JID, 1471 "log_granular_levels": {}, 1472 "log_rotate_max_bytes": 0, 1473 "log_rotate_backup_count": 0, 1474 "pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-master.pid"), 1475 "publish_session": 86400, 1476 "range_server": "range:80", 1477 "reactor": [], 1478 "reactor_refresh_interval": 60, 1479 "reactor_worker_threads": 10, 1480 "reactor_worker_hwm": 10000, 1481 "engines": [], 1482 "event_return": "", 1483 "event_return_queue": 0, 1484 "event_return_whitelist": [], 1485 "event_return_blacklist": [], 1486 "event_match_type": "startswith", 1487 "runner_returns": True, 1488 "serial": "msgpack", 1489 "test": False, 1490 "state_verbose": True, 1491 "state_output": "full", 1492 "state_output_diff": False, 1493 "state_output_profile": True, 1494 "state_auto_order": True, 1495 "state_events": False, 1496 "state_aggregate": False, 1497 "search": "", 1498 "loop_interval": 60, 1499 "nodegroups": {}, 1500 "ssh_list_nodegroups": {}, 1501 "ssh_use_home_key": False, 1502 "cython_enable": False, 1503 "enable_gpu_grains": False, 1504 # XXX: Remove 'key_logfile' support in 2014.1.0 1505 "key_logfile": os.path.join(salt.syspaths.LOGS_DIR, "key"), 1506 "verify_env": True, 1507 "permissive_pki_access": False, 1508 "key_pass": None, 1509 "signing_key_pass": None, 1510 "default_include": "master.d/*.conf", 1511 "winrepo_dir": os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo"), 1512 "winrepo_dir_ng": os.path.join( 1513 salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo-ng" 1514 ), 1515 "winrepo_cachefile": "winrepo.p", 1516 "winrepo_remotes": ["https://github.com/saltstack/salt-winrepo.git"], 1517 "winrepo_remotes_ng": ["https://github.com/saltstack/salt-winrepo-ng.git"], 1518 "winrepo_branch": "master", 1519 "winrepo_fallback": "", 1520 "winrepo_ssl_verify": True, 1521 "winrepo_user": "", 1522 "winrepo_password": "", 1523 "winrepo_insecure_auth": False, 1524 "winrepo_privkey": "", 1525 "winrepo_pubkey": "", 1526 "winrepo_passphrase": "", 1527 "winrepo_refspecs": _DFLT_REFSPECS, 1528 "syndic_wait": 5, 1529 "jinja_env": {}, 1530 "jinja_sls_env": {}, 1531 "jinja_lstrip_blocks": False, 1532 "jinja_trim_blocks": False, 1533 "tcp_keepalive": True, 1534 "tcp_keepalive_idle": 300, 1535 "tcp_keepalive_cnt": -1, 1536 "tcp_keepalive_intvl": -1, 1537 "sign_pub_messages": True, 1538 "keysize": 2048, 1539 "transport": "zeromq", 1540 "gather_job_timeout": 10, 1541 "syndic_event_forward_timeout": 0.5, 1542 "syndic_jid_forward_cache_hwm": 100, 1543 "regen_thin": False, 1544 "ssh_passwd": "", 1545 "ssh_priv_passwd": "", 1546 "ssh_port": "22", 1547 "ssh_sudo": False, 1548 "ssh_sudo_user": "", 1549 "ssh_timeout": 60, 1550 "ssh_user": "root", 1551 "ssh_scan_ports": "22", 1552 "ssh_scan_timeout": 0.01, 1553 "ssh_identities_only": False, 1554 "ssh_log_file": os.path.join(salt.syspaths.LOGS_DIR, "ssh"), 1555 "ssh_config_file": os.path.join(salt.syspaths.HOME_DIR, ".ssh", "config"), 1556 "cluster_mode": False, 1557 "sqlite_queue_dir": os.path.join(salt.syspaths.CACHE_DIR, "master", "queues"), 1558 "queue_dirs": [], 1559 "cli_summary": False, 1560 "max_minions": 0, 1561 "master_sign_key_name": "master_sign", 1562 "master_sign_pubkey": False, 1563 "master_pubkey_signature": "master_pubkey_signature", 1564 "master_use_pubkey_signature": False, 1565 "zmq_filtering": False, 1566 "zmq_monitor": False, 1567 "con_cache": False, 1568 "rotate_aes_key": True, 1569 "cache_sreqs": True, 1570 "dummy_pub": False, 1571 "http_connect_timeout": 20.0, # tornado default - 20 seconds 1572 "http_request_timeout": 1 * 60 * 60.0, # 1 hour 1573 "http_max_body": 100 * 1024 * 1024 * 1024, # 100GB 1574 "cache": "localfs", 1575 "memcache_expire_seconds": 0, 1576 "memcache_max_items": 1024, 1577 "memcache_full_cleanup": False, 1578 "memcache_debug": False, 1579 "thin_extra_mods": "", 1580 "min_extra_mods": "", 1581 "ssl": None, 1582 "extmod_whitelist": {}, 1583 "extmod_blacklist": {}, 1584 "clean_dynamic_modules": True, 1585 "django_auth_path": "", 1586 "django_auth_settings": "", 1587 "allow_minion_key_revoke": True, 1588 "salt_cp_chunk_size": 98304, 1589 "require_minion_sign_messages": False, 1590 "drop_messages_signature_fail": False, 1591 "discovery": False, 1592 "schedule": {}, 1593 "auth_events": True, 1594 "minion_data_cache_events": True, 1595 "enable_ssh_minions": False, 1596 "netapi_allow_raw_shell": False, 1597 "fips_mode": False, 1598 "detect_remote_minions": False, 1599 "remote_minions_port": 22, 1600 } 1601) 1602 1603 1604# ----- Salt Proxy Minion Configuration Defaults -----------------------------------> 1605# These are merged with DEFAULT_MINION_OPTS since many of them also apply here. 1606DEFAULT_PROXY_MINION_OPTS = immutabletypes.freeze( 1607 { 1608 "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "proxy"), 1609 "log_file": os.path.join(salt.syspaths.LOGS_DIR, "proxy"), 1610 "add_proxymodule_to_opts": False, 1611 "proxy_merge_grains_in_module": True, 1612 "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "proxy", "extmods"), 1613 "append_minionid_config_dirs": [ 1614 "cachedir", 1615 "pidfile", 1616 "default_include", 1617 "extension_modules", 1618 ], 1619 "default_include": "proxy.d/*.conf", 1620 "proxy_merge_pillar_in_opts": False, 1621 "proxy_deep_merge_pillar_in_opts": False, 1622 "proxy_merge_pillar_in_opts_strategy": "smart", 1623 "proxy_mines_pillar": True, 1624 # By default, proxies will preserve the connection. 1625 # If this option is set to False, 1626 # the connection with the remote dumb device 1627 # is closed after each command request. 1628 "proxy_always_alive": True, 1629 "proxy_keep_alive": True, # by default will try to keep alive the connection 1630 "proxy_keep_alive_interval": 1, # frequency of the proxy keepalive in minutes 1631 "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "proxy"), 1632 "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "proxy"), 1633 "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "proxy"), 1634 } 1635) 1636 1637# ----- Salt Cloud Configuration Defaults -----------------------------------> 1638DEFAULT_CLOUD_OPTS = immutabletypes.freeze( 1639 { 1640 "verify_env": True, 1641 "default_include": "cloud.conf.d/*.conf", 1642 # Global defaults 1643 "ssh_auth": "", 1644 "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "cloud"), 1645 "keysize": 4096, 1646 "os": "", 1647 "script": "bootstrap-salt", 1648 "start_action": None, 1649 "enable_hard_maps": False, 1650 "delete_sshkeys": False, 1651 # Custom deploy scripts 1652 "deploy_scripts_search_path": "cloud.deploy.d", 1653 # Logging defaults 1654 "log_file": os.path.join(salt.syspaths.LOGS_DIR, "cloud"), 1655 "log_level": "warning", 1656 "log_level_logfile": None, 1657 "log_datefmt": _DFLT_LOG_DATEFMT, 1658 "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, 1659 "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, 1660 "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, 1661 "log_fmt_jid": _DFLT_LOG_FMT_JID, 1662 "log_granular_levels": {}, 1663 "log_rotate_max_bytes": 0, 1664 "log_rotate_backup_count": 0, 1665 "bootstrap_delay": 0, 1666 "cache": "localfs", 1667 } 1668) 1669 1670DEFAULT_API_OPTS = immutabletypes.freeze( 1671 { 1672 # ----- Salt master settings overridden by Salt-API ---------------------> 1673 "api_pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-api.pid"), 1674 "api_logfile": os.path.join(salt.syspaths.LOGS_DIR, "api"), 1675 "rest_timeout": 300, 1676 # <---- Salt master settings overridden by Salt-API ---------------------- 1677 } 1678) 1679 1680DEFAULT_SPM_OPTS = immutabletypes.freeze( 1681 { 1682 # ----- Salt master settings overridden by SPM ---------------------> 1683 "spm_conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "spm"), 1684 "formula_path": salt.syspaths.SPM_FORMULA_PATH, 1685 "pillar_path": salt.syspaths.SPM_PILLAR_PATH, 1686 "reactor_path": salt.syspaths.SPM_REACTOR_PATH, 1687 "spm_logfile": os.path.join(salt.syspaths.LOGS_DIR, "spm"), 1688 "spm_default_include": "spm.d/*.conf", 1689 # spm_repos_config also includes a .d/ directory 1690 "spm_repos_config": "/etc/salt/spm.repos", 1691 "spm_cache_dir": os.path.join(salt.syspaths.CACHE_DIR, "spm"), 1692 "spm_build_dir": os.path.join(salt.syspaths.SRV_ROOT_DIR, "spm_build"), 1693 "spm_build_exclude": ["CVS", ".hg", ".git", ".svn"], 1694 "spm_db": os.path.join(salt.syspaths.CACHE_DIR, "spm", "packages.db"), 1695 "cache": "localfs", 1696 "spm_repo_dups": "ignore", 1697 # If set, spm_node_type will be either master or minion, but they should 1698 # NOT be a default 1699 "spm_node_type": "", 1700 "spm_share_dir": os.path.join(salt.syspaths.SHARE_DIR, "spm"), 1701 # <---- Salt master settings overridden by SPM ---------------------- 1702 } 1703) 1704 1705VM_CONFIG_DEFAULTS = immutabletypes.freeze( 1706 {"default_include": "cloud.profiles.d/*.conf"} 1707) 1708 1709PROVIDER_CONFIG_DEFAULTS = immutabletypes.freeze( 1710 {"default_include": "cloud.providers.d/*.conf"} 1711) 1712# <---- Salt Cloud Configuration Defaults ------------------------------------ 1713 1714 1715def _normalize_roots(file_roots): 1716 """ 1717 Normalize file or pillar roots. 1718 """ 1719 for saltenv, dirs in file_roots.items(): 1720 normalized_saltenv = str(saltenv) 1721 if normalized_saltenv != saltenv: 1722 file_roots[normalized_saltenv] = file_roots.pop(saltenv) 1723 if not isinstance(dirs, (list, tuple)): 1724 file_roots[normalized_saltenv] = [] 1725 file_roots[normalized_saltenv] = _expand_glob_path( 1726 file_roots[normalized_saltenv] 1727 ) 1728 return file_roots 1729 1730 1731def _validate_pillar_roots(pillar_roots): 1732 """ 1733 If the pillar_roots option has a key that is None then we will error out, 1734 just replace it with an empty list 1735 """ 1736 if not isinstance(pillar_roots, dict): 1737 log.warning( 1738 "The pillar_roots parameter is not properly formatted, using defaults" 1739 ) 1740 return {"base": _expand_glob_path([salt.syspaths.BASE_PILLAR_ROOTS_DIR])} 1741 return _normalize_roots(pillar_roots) 1742 1743 1744def _validate_file_roots(file_roots): 1745 """ 1746 If the file_roots option has a key that is None then we will error out, 1747 just replace it with an empty list 1748 """ 1749 if not isinstance(file_roots, dict): 1750 log.warning( 1751 "The file_roots parameter is not properly formatted, using defaults" 1752 ) 1753 return {"base": _expand_glob_path([salt.syspaths.BASE_FILE_ROOTS_DIR])} 1754 return _normalize_roots(file_roots) 1755 1756 1757def _expand_glob_path(file_roots): 1758 """ 1759 Applies shell globbing to a set of directories and returns 1760 the expanded paths 1761 """ 1762 unglobbed_path = [] 1763 for path in file_roots: 1764 try: 1765 if glob.has_magic(path): 1766 unglobbed_path.extend(glob.glob(path)) 1767 else: 1768 unglobbed_path.append(path) 1769 except Exception: # pylint: disable=broad-except 1770 unglobbed_path.append(path) 1771 return unglobbed_path 1772 1773 1774def _validate_opts(opts): 1775 """ 1776 Check that all of the types of values passed into the config are 1777 of the right types 1778 """ 1779 1780 def format_multi_opt(valid_type): 1781 try: 1782 num_types = len(valid_type) 1783 except TypeError: 1784 # Bare type name won't have a length, return the name of the type 1785 # passed. 1786 return valid_type.__name__ 1787 else: 1788 1789 def get_types(types, type_tuple): 1790 for item in type_tuple: 1791 if isinstance(item, tuple): 1792 get_types(types, item) 1793 else: 1794 try: 1795 types.append(item.__name__) 1796 except AttributeError: 1797 log.warning( 1798 "Unable to interpret type %s while validating " 1799 "configuration", 1800 item, 1801 ) 1802 1803 types = [] 1804 get_types(types, valid_type) 1805 1806 ret = ", ".join(types[:-1]) 1807 ret += " or " + types[-1] 1808 return ret 1809 1810 errors = [] 1811 1812 err = ( 1813 "Config option '{}' with value {} has an invalid type of {}, a " 1814 "{} is required for this option" 1815 ) 1816 for key, val in opts.items(): 1817 if key in VALID_OPTS: 1818 if val is None: 1819 if VALID_OPTS[key] is None: 1820 continue 1821 else: 1822 try: 1823 if None in VALID_OPTS[key]: 1824 continue 1825 except TypeError: 1826 # VALID_OPTS[key] is not iterable and not None 1827 pass 1828 1829 if isinstance(val, VALID_OPTS[key]): 1830 continue 1831 1832 # We don't know what data type sdb will return at run-time so we 1833 # simply cannot check it for correctness here at start-time. 1834 if isinstance(val, str) and val.startswith("sdb://"): 1835 continue 1836 1837 if hasattr(VALID_OPTS[key], "__call__"): 1838 try: 1839 VALID_OPTS[key](val) 1840 if isinstance(val, (list, dict)): 1841 # We'll only get here if VALID_OPTS[key] is str or 1842 # bool, and the passed value is a list/dict. Attempting 1843 # to run int() or float() on a list/dict will raise an 1844 # exception, but running str() or bool() on it will 1845 # pass despite not being the correct type. 1846 errors.append( 1847 err.format( 1848 key, val, type(val).__name__, VALID_OPTS[key].__name__ 1849 ) 1850 ) 1851 except (TypeError, ValueError): 1852 errors.append( 1853 err.format( 1854 key, val, type(val).__name__, VALID_OPTS[key].__name__ 1855 ) 1856 ) 1857 continue 1858 1859 errors.append( 1860 err.format( 1861 key, val, type(val).__name__, format_multi_opt(VALID_OPTS[key]) 1862 ) 1863 ) 1864 1865 # Convert list to comma-delimited string for 'return' config option 1866 if isinstance(opts.get("return"), list): 1867 opts["return"] = ",".join(opts["return"]) 1868 1869 for error in errors: 1870 log.warning(error) 1871 if errors: 1872 return False 1873 return True 1874 1875 1876def _validate_ssh_minion_opts(opts): 1877 """ 1878 Ensure we're not using any invalid ssh_minion_opts. We want to make sure 1879 that the ssh_minion_opts does not override any pillar or fileserver options 1880 inherited from the master config. To add other items, modify the if 1881 statement in the for loop below. 1882 """ 1883 ssh_minion_opts = opts.get("ssh_minion_opts", {}) 1884 if not isinstance(ssh_minion_opts, dict): 1885 log.error("Invalidly-formatted ssh_minion_opts") 1886 opts.pop("ssh_minion_opts") 1887 1888 for opt_name in list(ssh_minion_opts): 1889 if ( 1890 re.match("^[a-z0-9]+fs_", opt_name, flags=re.IGNORECASE) 1891 or ("pillar" in opt_name and not "ssh_merge_pillar" == opt_name) 1892 or opt_name in ("fileserver_backend",) 1893 ): 1894 log.warning( 1895 "'%s' is not a valid ssh_minion_opts parameter, ignoring", opt_name 1896 ) 1897 ssh_minion_opts.pop(opt_name) 1898 1899 1900def _append_domain(opts): 1901 """ 1902 Append a domain to the existing id if it doesn't already exist 1903 """ 1904 # Domain already exists 1905 if opts["id"].endswith(opts["append_domain"]): 1906 return opts["id"] 1907 # Trailing dot should mean an FQDN that is terminated, leave it alone. 1908 if opts["id"].endswith("."): 1909 return opts["id"] 1910 return "{0[id]}.{0[append_domain]}".format(opts) 1911 1912 1913def _read_conf_file(path): 1914 """ 1915 Read in a config file from a given path and process it into a dictionary 1916 """ 1917 log.debug("Reading configuration from %s", path) 1918 append_file_suffix_YAMLError = False 1919 with salt.utils.files.fopen(path, "r") as conf_file: 1920 try: 1921 conf_opts = salt.utils.yaml.safe_load(conf_file) or {} 1922 except salt.utils.yaml.YAMLError as err: 1923 message = "Error parsing configuration file: {} - {}".format(path, err) 1924 log.error(message) 1925 if path.endswith("_schedule.conf"): 1926 # Create empty dictionary of config options 1927 conf_opts = {} 1928 # Rename this file, once closed 1929 append_file_suffix_YAMLError = True 1930 else: 1931 raise salt.exceptions.SaltConfigurationError(message) 1932 1933 if append_file_suffix_YAMLError: 1934 message = "Renaming to {}".format(path + "YAMLError") 1935 log.error(message) 1936 os.replace(path, path + "YAMLError") 1937 1938 # only interpret documents as a valid conf, not things like strings, 1939 # which might have been caused by invalid yaml syntax 1940 if not isinstance(conf_opts, dict): 1941 message = ( 1942 "Error parsing configuration file: {} - conf " 1943 "should be a document, not {}.".format(path, type(conf_opts)) 1944 ) 1945 log.error(message) 1946 raise salt.exceptions.SaltConfigurationError(message) 1947 1948 # allow using numeric ids: convert int to string 1949 if "id" in conf_opts: 1950 if not isinstance(conf_opts["id"], str): 1951 conf_opts["id"] = str(conf_opts["id"]) 1952 else: 1953 conf_opts["id"] = salt.utils.data.decode(conf_opts["id"]) 1954 return conf_opts 1955 1956 1957def _absolute_path(path, relative_to=None): 1958 """ 1959 Return an absolute path. In case ``relative_to`` is passed and ``path`` is 1960 not an absolute path, we try to prepend ``relative_to`` to ``path``and if 1961 that path exists, return that one 1962 """ 1963 1964 if path and os.path.isabs(path): 1965 return path 1966 if path and relative_to is not None: 1967 _abspath = os.path.join(relative_to, path) 1968 if os.path.isfile(_abspath): 1969 log.debug( 1970 "Relative path '%s' converted to existing absolute path '%s'", 1971 path, 1972 _abspath, 1973 ) 1974 return _abspath 1975 return path 1976 1977 1978def load_config(path, env_var, default_path=None, exit_on_config_errors=True): 1979 """ 1980 Returns configuration dict from parsing either the file described by 1981 ``path`` or the environment variable described by ``env_var`` as YAML. 1982 """ 1983 if path is None: 1984 # When the passed path is None, we just want the configuration 1985 # defaults, not actually loading the whole configuration. 1986 return {} 1987 1988 if default_path is None: 1989 # This is most likely not being used from salt, i.e., could be salt-cloud 1990 # or salt-api which have not yet migrated to the new default_path 1991 # argument. Let's issue a warning message that the environ vars won't 1992 # work. 1993 import inspect 1994 1995 previous_frame = inspect.getframeinfo(inspect.currentframe().f_back) 1996 log.warning( 1997 "The function '%s()' defined in '%s' is not yet using the " 1998 "new 'default_path' argument to `salt.config.load_config()`. " 1999 "As such, the '%s' environment variable will be ignored", 2000 previous_frame.function, 2001 previous_frame.filename, 2002 env_var, 2003 ) 2004 # In this case, maintain old behavior 2005 default_path = DEFAULT_MASTER_OPTS["conf_file"] 2006 2007 # Default to the environment variable path, if it exists 2008 env_path = os.environ.get(env_var, path) 2009 if not env_path or not os.path.isfile(env_path): 2010 env_path = path 2011 # If non-default path from `-c`, use that over the env variable 2012 if path != default_path: 2013 env_path = path 2014 2015 path = env_path 2016 2017 # If the configuration file is missing, attempt to copy the template, 2018 # after removing the first header line. 2019 if not os.path.isfile(path): 2020 template = "{}.template".format(path) 2021 if os.path.isfile(template): 2022 log.debug("Writing %s based on %s", path, template) 2023 with salt.utils.files.fopen(path, "w") as out: 2024 with salt.utils.files.fopen(template, "r") as ifile: 2025 ifile.readline() # skip first line 2026 out.write(ifile.read()) 2027 2028 opts = {} 2029 2030 if salt.utils.validate.path.is_readable(path): 2031 try: 2032 opts = _read_conf_file(path) 2033 opts["conf_file"] = path 2034 except salt.exceptions.SaltConfigurationError as error: 2035 log.error(error) 2036 if exit_on_config_errors: 2037 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 2038 else: 2039 log.debug("Missing configuration file: %s", path) 2040 2041 return opts 2042 2043 2044def include_config(include, orig_path, verbose, exit_on_config_errors=False): 2045 """ 2046 Parses extra configuration file(s) specified in an include list in the 2047 main config file. 2048 """ 2049 # Protect against empty option 2050 if not include: 2051 return {} 2052 2053 if orig_path is None: 2054 # When the passed path is None, we just want the configuration 2055 # defaults, not actually loading the whole configuration. 2056 return {} 2057 2058 if isinstance(include, str): 2059 include = [include] 2060 2061 configuration = {} 2062 for path in include: 2063 # Allow for includes like ~/foo 2064 path = os.path.expanduser(path) 2065 if not os.path.isabs(path): 2066 path = os.path.join(os.path.dirname(orig_path), path) 2067 2068 # Catch situation where user typos path in configuration; also warns 2069 # for empty include directory (which might be by design) 2070 glob_matches = glob.glob(path) 2071 if not glob_matches: 2072 if verbose: 2073 log.warning( 2074 'Warning parsing configuration file: "include" path/glob ' 2075 "'%s' matches no files", 2076 path, 2077 ) 2078 2079 for fn_ in sorted(glob_matches): 2080 log.debug("Including configuration from '%s'", fn_) 2081 try: 2082 opts = _read_conf_file(fn_) 2083 except salt.exceptions.SaltConfigurationError as error: 2084 log.error(error) 2085 if exit_on_config_errors: 2086 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 2087 else: 2088 # Initialize default config if we wish to skip config errors 2089 opts = {} 2090 schedule = opts.get("schedule", {}) 2091 if schedule and "schedule" in configuration: 2092 configuration["schedule"].update(schedule) 2093 include = opts.get("include", []) 2094 if include: 2095 opts.update(include_config(include, fn_, verbose)) 2096 2097 salt.utils.dictupdate.update(configuration, opts, True, True) 2098 2099 return configuration 2100 2101 2102def prepend_root_dir(opts, path_options): 2103 """ 2104 Prepends the options that represent filesystem paths with value of the 2105 'root_dir' option. 2106 """ 2107 root_dir = os.path.abspath(opts["root_dir"]) 2108 def_root_dir = salt.syspaths.ROOT_DIR.rstrip(os.sep) 2109 for path_option in path_options: 2110 if path_option in opts: 2111 path = opts[path_option] 2112 tmp_path_def_root_dir = None 2113 tmp_path_root_dir = None 2114 # When running testsuite, salt.syspaths.ROOT_DIR is often empty 2115 if path == def_root_dir or path.startswith(def_root_dir + os.sep): 2116 # Remove the default root dir prefix 2117 tmp_path_def_root_dir = path[len(def_root_dir) :] 2118 if root_dir and (path == root_dir or path.startswith(root_dir + os.sep)): 2119 # Remove the root dir prefix 2120 tmp_path_root_dir = path[len(root_dir) :] 2121 if tmp_path_def_root_dir and not tmp_path_root_dir: 2122 # Just the default root dir matched 2123 path = tmp_path_def_root_dir 2124 elif tmp_path_root_dir and not tmp_path_def_root_dir: 2125 # Just the root dir matched 2126 path = tmp_path_root_dir 2127 elif tmp_path_def_root_dir and tmp_path_root_dir: 2128 # In this case both the default root dir and the override root 2129 # dir matched; this means that either 2130 # def_root_dir is a substring of root_dir or vice versa 2131 # We must choose the most specific path 2132 if def_root_dir in root_dir: 2133 path = tmp_path_root_dir 2134 else: 2135 path = tmp_path_def_root_dir 2136 elif salt.utils.platform.is_windows() and not os.path.splitdrive(path)[0]: 2137 # In windows, os.path.isabs resolves '/' to 'C:\\' or whatever 2138 # the root drive is. This elif prevents the next from being 2139 # hit, so that the root_dir is prefixed in cases where the 2140 # drive is not prefixed on a config option 2141 pass 2142 elif os.path.isabs(path): 2143 # Absolute path (not default or overridden root_dir) 2144 # No prepending required 2145 continue 2146 # Prepending the root dir 2147 opts[path_option] = salt.utils.path.join(root_dir, path) 2148 2149 2150def insert_system_path(opts, paths): 2151 """ 2152 Inserts path into python path taking into consideration 'root_dir' option. 2153 """ 2154 if isinstance(paths, str): 2155 paths = [paths] 2156 for path in paths: 2157 path_options = {"path": path, "root_dir": opts["root_dir"]} 2158 prepend_root_dir(path_options, path_options) 2159 if os.path.isdir(path_options["path"]) and path_options["path"] not in sys.path: 2160 sys.path.insert(0, path_options["path"]) 2161 2162 2163def minion_config( 2164 path, 2165 env_var="SALT_MINION_CONFIG", 2166 defaults=None, 2167 cache_minion_id=False, 2168 ignore_config_errors=True, 2169 minion_id=None, 2170 role="minion", 2171): 2172 """ 2173 Reads in the minion configuration file and sets up special options 2174 2175 This is useful for Minion-side operations, such as the 2176 :py:class:`~salt.client.Caller` class, and manually running the loader 2177 interface. 2178 2179 .. code-block:: python 2180 2181 import salt.config 2182 minion_opts = salt.config.minion_config('/etc/salt/minion') 2183 """ 2184 if defaults is None: 2185 defaults = DEFAULT_MINION_OPTS.copy() 2186 2187 if not os.environ.get(env_var, None): 2188 # No valid setting was given using the configuration variable. 2189 # Lets see is SALT_CONFIG_DIR is of any use 2190 salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) 2191 if salt_config_dir: 2192 env_config_file_path = os.path.join(salt_config_dir, "minion") 2193 if salt_config_dir and os.path.isfile(env_config_file_path): 2194 # We can get a configuration file using SALT_CONFIG_DIR, let's 2195 # update the environment with this information 2196 os.environ[env_var] = env_config_file_path 2197 2198 overrides = load_config(path, env_var, DEFAULT_MINION_OPTS["conf_file"]) 2199 default_include = overrides.get("default_include", defaults["default_include"]) 2200 include = overrides.get("include", []) 2201 2202 overrides.update( 2203 include_config( 2204 default_include, 2205 path, 2206 verbose=False, 2207 exit_on_config_errors=not ignore_config_errors, 2208 ) 2209 ) 2210 overrides.update( 2211 include_config( 2212 include, path, verbose=True, exit_on_config_errors=not ignore_config_errors 2213 ) 2214 ) 2215 2216 opts = apply_minion_config( 2217 overrides, defaults, cache_minion_id=cache_minion_id, minion_id=minion_id 2218 ) 2219 opts["__role"] = role 2220 if role != "master": 2221 apply_sdb(opts) 2222 _validate_opts(opts) 2223 return opts 2224 2225 2226def mminion_config(path, overrides, ignore_config_errors=True): 2227 opts = minion_config(path, ignore_config_errors=ignore_config_errors, role="master") 2228 opts.update(overrides) 2229 apply_sdb(opts) 2230 2231 _validate_opts(opts) 2232 opts["grains"] = salt.loader.grains(opts) 2233 opts["pillar"] = {} 2234 return opts 2235 2236 2237def proxy_config( 2238 path, 2239 env_var="SALT_PROXY_CONFIG", 2240 defaults=None, 2241 cache_minion_id=False, 2242 ignore_config_errors=True, 2243 minion_id=None, 2244): 2245 """ 2246 Reads in the proxy minion configuration file and sets up special options 2247 2248 This is useful for Minion-side operations, such as the 2249 :py:class:`~salt.client.Caller` class, and manually running the loader 2250 interface. 2251 2252 .. code-block:: python 2253 2254 import salt.config 2255 proxy_opts = salt.config.proxy_config('/etc/salt/proxy') 2256 """ 2257 if defaults is None: 2258 defaults = DEFAULT_MINION_OPTS.copy() 2259 2260 defaults.update(DEFAULT_PROXY_MINION_OPTS) 2261 2262 if not os.environ.get(env_var, None): 2263 # No valid setting was given using the configuration variable. 2264 # Lets see is SALT_CONFIG_DIR is of any use 2265 salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) 2266 if salt_config_dir: 2267 env_config_file_path = os.path.join(salt_config_dir, "proxy") 2268 if salt_config_dir and os.path.isfile(env_config_file_path): 2269 # We can get a configuration file using SALT_CONFIG_DIR, let's 2270 # update the environment with this information 2271 os.environ[env_var] = env_config_file_path 2272 2273 overrides = load_config(path, env_var, DEFAULT_PROXY_MINION_OPTS["conf_file"]) 2274 default_include = overrides.get("default_include", defaults["default_include"]) 2275 include = overrides.get("include", []) 2276 2277 overrides.update( 2278 include_config( 2279 default_include, 2280 path, 2281 verbose=False, 2282 exit_on_config_errors=not ignore_config_errors, 2283 ) 2284 ) 2285 overrides.update( 2286 include_config( 2287 include, path, verbose=True, exit_on_config_errors=not ignore_config_errors 2288 ) 2289 ) 2290 2291 opts = apply_minion_config( 2292 overrides, defaults, cache_minion_id=cache_minion_id, minion_id=minion_id 2293 ) 2294 2295 # Update opts with proxy specific configuration 2296 # with the updated default_include. 2297 default_include = opts.get("default_include", defaults["default_include"]) 2298 include = opts.get("include", []) 2299 2300 overrides.update( 2301 include_config( 2302 default_include, 2303 path, 2304 verbose=False, 2305 exit_on_config_errors=not ignore_config_errors, 2306 ) 2307 ) 2308 overrides.update( 2309 include_config( 2310 include, path, verbose=True, exit_on_config_errors=not ignore_config_errors 2311 ) 2312 ) 2313 2314 opts = apply_minion_config( 2315 overrides, defaults, cache_minion_id=cache_minion_id, minion_id=minion_id 2316 ) 2317 2318 apply_sdb(opts) 2319 _validate_opts(opts) 2320 return opts 2321 2322 2323def syndic_config( 2324 master_config_path, 2325 minion_config_path, 2326 master_env_var="SALT_MASTER_CONFIG", 2327 minion_env_var="SALT_MINION_CONFIG", 2328 minion_defaults=None, 2329 master_defaults=None, 2330): 2331 2332 if minion_defaults is None: 2333 minion_defaults = DEFAULT_MINION_OPTS.copy() 2334 2335 if master_defaults is None: 2336 master_defaults = DEFAULT_MASTER_OPTS.copy() 2337 2338 opts = {} 2339 master_opts = master_config(master_config_path, master_env_var, master_defaults) 2340 minion_opts = minion_config(minion_config_path, minion_env_var, minion_defaults) 2341 opts["_minion_conf_file"] = master_opts["conf_file"] 2342 opts["_master_conf_file"] = minion_opts["conf_file"] 2343 opts.update(master_opts) 2344 opts.update(minion_opts) 2345 syndic_opts = { 2346 "__role": "syndic", 2347 "root_dir": opts.get("root_dir", salt.syspaths.ROOT_DIR), 2348 "pidfile": opts.get("syndic_pidfile", "salt-syndic.pid"), 2349 "log_file": opts.get("syndic_log_file", "salt-syndic.log"), 2350 "log_level": master_opts["log_level"], 2351 "id": minion_opts["id"], 2352 "pki_dir": minion_opts["pki_dir"], 2353 "master": opts["syndic_master"], 2354 "interface": master_opts["interface"], 2355 "master_port": int( 2356 opts.get( 2357 # The user has explicitly defined the syndic master port 2358 "syndic_master_port", 2359 opts.get( 2360 # No syndic_master_port, grab master_port from opts 2361 "master_port", 2362 # No master_opts, grab from the provided minion defaults 2363 minion_defaults.get( 2364 "master_port", 2365 # Not on the provided minion defaults, load from the 2366 # static minion defaults 2367 DEFAULT_MINION_OPTS["master_port"], 2368 ), 2369 ), 2370 ) 2371 ), 2372 "user": opts.get("syndic_user", opts["user"]), 2373 "sock_dir": os.path.join( 2374 opts["cachedir"], opts.get("syndic_sock_dir", opts["sock_dir"]) 2375 ), 2376 "sock_pool_size": master_opts["sock_pool_size"], 2377 "cachedir": master_opts["cachedir"], 2378 } 2379 opts.update(syndic_opts) 2380 # Prepend root_dir to other paths 2381 prepend_root_dirs = [ 2382 "pki_dir", 2383 "cachedir", 2384 "pidfile", 2385 "sock_dir", 2386 "extension_modules", 2387 "autosign_file", 2388 "autoreject_file", 2389 "token_dir", 2390 "autosign_grains_dir", 2391 ] 2392 for config_key in ("log_file", "key_logfile", "syndic_log_file"): 2393 # If this is not a URI and instead a local path 2394 if urllib.parse.urlparse(opts.get(config_key, "")).scheme == "": 2395 prepend_root_dirs.append(config_key) 2396 prepend_root_dir(opts, prepend_root_dirs) 2397 return opts 2398 2399 2400def apply_sdb(opts, sdb_opts=None): 2401 """ 2402 Recurse for sdb:// links for opts 2403 """ 2404 # Late load of SDB to keep CLI light 2405 import salt.utils.sdb 2406 2407 if sdb_opts is None: 2408 sdb_opts = opts 2409 if isinstance(sdb_opts, str) and sdb_opts.startswith("sdb://"): 2410 return salt.utils.sdb.sdb_get(sdb_opts, opts) 2411 elif isinstance(sdb_opts, dict): 2412 for key, value in sdb_opts.items(): 2413 if value is None: 2414 continue 2415 sdb_opts[key] = apply_sdb(opts, value) 2416 elif isinstance(sdb_opts, list): 2417 for key, value in enumerate(sdb_opts): 2418 if value is None: 2419 continue 2420 sdb_opts[key] = apply_sdb(opts, value) 2421 2422 return sdb_opts 2423 2424 2425# ----- Salt Cloud Configuration Functions ----------------------------------> 2426def cloud_config( 2427 path, 2428 env_var="SALT_CLOUD_CONFIG", 2429 defaults=None, 2430 master_config_path=None, 2431 master_config=None, 2432 providers_config_path=None, 2433 providers_config=None, 2434 profiles_config_path=None, 2435 profiles_config=None, 2436): 2437 """ 2438 Read in the Salt Cloud config and return the dict 2439 """ 2440 if path: 2441 config_dir = os.path.dirname(path) 2442 else: 2443 config_dir = salt.syspaths.CONFIG_DIR 2444 2445 # Load the cloud configuration 2446 overrides = load_config(path, env_var, os.path.join(config_dir, "cloud")) 2447 2448 if defaults is None: 2449 defaults = DEFAULT_CLOUD_OPTS.copy() 2450 2451 # Set defaults early to override Salt Master's default config values later 2452 defaults.update(overrides) 2453 overrides = defaults 2454 2455 # Load cloud configuration from any default or provided includes 2456 overrides.update( 2457 salt.config.include_config(overrides["default_include"], path, verbose=False) 2458 ) 2459 include = overrides.get("include", []) 2460 overrides.update(salt.config.include_config(include, path, verbose=True)) 2461 2462 # The includes have been evaluated, let's see if master, providers and 2463 # profiles configuration settings have been included and if not, set the 2464 # default value 2465 if "master_config" in overrides and master_config_path is None: 2466 # The configuration setting is being specified in the main cloud 2467 # configuration file 2468 master_config_path = overrides["master_config"] 2469 elif ( 2470 "master_config" not in overrides 2471 and not master_config 2472 and not master_config_path 2473 ): 2474 # The configuration setting is not being provided in the main cloud 2475 # configuration file, and 2476 master_config_path = os.path.join(config_dir, "master") 2477 2478 # Convert relative to absolute paths if necessary 2479 master_config_path = _absolute_path(master_config_path, config_dir) 2480 2481 if "providers_config" in overrides and providers_config_path is None: 2482 # The configuration setting is being specified in the main cloud 2483 # configuration file 2484 providers_config_path = overrides["providers_config"] 2485 elif ( 2486 "providers_config" not in overrides 2487 and not providers_config 2488 and not providers_config_path 2489 ): 2490 providers_config_path = os.path.join(config_dir, "cloud.providers") 2491 2492 # Convert relative to absolute paths if necessary 2493 providers_config_path = _absolute_path(providers_config_path, config_dir) 2494 2495 if "profiles_config" in overrides and profiles_config_path is None: 2496 # The configuration setting is being specified in the main cloud 2497 # configuration file 2498 profiles_config_path = overrides["profiles_config"] 2499 elif ( 2500 "profiles_config" not in overrides 2501 and not profiles_config 2502 and not profiles_config_path 2503 ): 2504 profiles_config_path = os.path.join(config_dir, "cloud.profiles") 2505 2506 # Convert relative to absolute paths if necessary 2507 profiles_config_path = _absolute_path(profiles_config_path, config_dir) 2508 2509 # Prepare the deploy scripts search path 2510 deploy_scripts_search_path = overrides.get( 2511 "deploy_scripts_search_path", 2512 defaults.get("deploy_scripts_search_path", "cloud.deploy.d"), 2513 ) 2514 if isinstance(deploy_scripts_search_path, str): 2515 deploy_scripts_search_path = [deploy_scripts_search_path] 2516 2517 # Check the provided deploy scripts search path removing any non existing 2518 # entries. 2519 for idx, entry in enumerate(deploy_scripts_search_path[:]): 2520 if not os.path.isabs(entry): 2521 # Let's try adding the provided path's directory name turns the 2522 # entry into a proper directory 2523 entry = os.path.join(os.path.dirname(path), entry) 2524 2525 if os.path.isdir(entry): 2526 # Path exists, let's update the entry (its path might have been 2527 # made absolute) 2528 deploy_scripts_search_path[idx] = entry 2529 continue 2530 2531 # It's not a directory? Remove it from the search path 2532 deploy_scripts_search_path.pop(idx) 2533 2534 # Add the built-in scripts directory to the search path (last resort) 2535 deploy_scripts_search_path.append( 2536 os.path.abspath( 2537 os.path.join(os.path.dirname(__file__), "..", "cloud", "deploy") 2538 ) 2539 ) 2540 2541 # Let's make the search path a tuple and add it to the overrides. 2542 overrides.update(deploy_scripts_search_path=tuple(deploy_scripts_search_path)) 2543 2544 # Grab data from the 4 sources 2545 # 1st - Master config 2546 if master_config_path is not None and master_config is not None: 2547 raise salt.exceptions.SaltCloudConfigError( 2548 "Only pass `master_config` or `master_config_path`, not both." 2549 ) 2550 elif master_config_path is None and master_config is None: 2551 master_config = salt.config.master_config( 2552 overrides.get( 2553 # use the value from the cloud config file 2554 "master_config", 2555 # if not found, use the default path 2556 os.path.join(salt.syspaths.CONFIG_DIR, "master"), 2557 ) 2558 ) 2559 elif master_config_path is not None and master_config is None: 2560 master_config = salt.config.master_config(master_config_path) 2561 2562 # cloud config has a separate cachedir 2563 del master_config["cachedir"] 2564 2565 # 2nd - salt-cloud configuration which was loaded before so we could 2566 # extract the master configuration file if needed. 2567 2568 # Override master configuration with the salt cloud(current overrides) 2569 master_config.update(overrides) 2570 # We now set the overridden master_config as the overrides 2571 overrides = master_config 2572 2573 if providers_config_path is not None and providers_config is not None: 2574 raise salt.exceptions.SaltCloudConfigError( 2575 "Only pass `providers_config` or `providers_config_path`, not both." 2576 ) 2577 elif providers_config_path is None and providers_config is None: 2578 providers_config_path = overrides.get( 2579 # use the value from the cloud config file 2580 "providers_config", 2581 # if not found, use the default path 2582 os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers"), 2583 ) 2584 2585 if profiles_config_path is not None and profiles_config is not None: 2586 raise salt.exceptions.SaltCloudConfigError( 2587 "Only pass `profiles_config` or `profiles_config_path`, not both." 2588 ) 2589 elif profiles_config_path is None and profiles_config is None: 2590 profiles_config_path = overrides.get( 2591 # use the value from the cloud config file 2592 "profiles_config", 2593 # if not found, use the default path 2594 os.path.join(salt.syspaths.CONFIG_DIR, "cloud.profiles"), 2595 ) 2596 2597 # Apply the salt-cloud configuration 2598 opts = apply_cloud_config(overrides, defaults) 2599 2600 # 3rd - Include Cloud Providers 2601 if "providers" in opts: 2602 if providers_config is not None: 2603 raise salt.exceptions.SaltCloudConfigError( 2604 "Do not mix the old cloud providers configuration with " 2605 "the passing a pre-configured providers configuration " 2606 "dictionary." 2607 ) 2608 2609 if providers_config_path is not None: 2610 providers_confd = os.path.join( 2611 os.path.dirname(providers_config_path), "cloud.providers.d", "*" 2612 ) 2613 2614 if os.path.isfile(providers_config_path) or glob.glob(providers_confd): 2615 raise salt.exceptions.SaltCloudConfigError( 2616 "Do not mix the old cloud providers configuration with " 2617 "the new one. The providers configuration should now go " 2618 "in the file `{0}` or a separate `*.conf` file within " 2619 "`cloud.providers.d/` which is relative to `{0}`.".format( 2620 os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers") 2621 ) 2622 ) 2623 # No exception was raised? It's the old configuration alone 2624 providers_config = opts["providers"] 2625 2626 elif providers_config_path is not None: 2627 # Load from configuration file, even if that files does not exist since 2628 # it will be populated with defaults. 2629 providers_config = cloud_providers_config(providers_config_path) 2630 2631 # Let's assign back the computed providers configuration 2632 opts["providers"] = providers_config 2633 2634 # 4th - Include VM profiles config 2635 if profiles_config is None: 2636 # Load profiles configuration from the provided file 2637 profiles_config = vm_profiles_config(profiles_config_path, providers_config) 2638 opts["profiles"] = profiles_config 2639 2640 # recurse opts for sdb configs 2641 apply_sdb(opts) 2642 2643 # prepend root_dir 2644 prepend_root_dirs = ["cachedir"] 2645 if "log_file" in opts and urllib.parse.urlparse(opts["log_file"]).scheme == "": 2646 prepend_root_dirs.append(opts["log_file"]) 2647 prepend_root_dir(opts, prepend_root_dirs) 2648 2649 # Return the final options 2650 return opts 2651 2652 2653def apply_cloud_config(overrides, defaults=None): 2654 """ 2655 Return a cloud config 2656 """ 2657 if defaults is None: 2658 defaults = DEFAULT_CLOUD_OPTS.copy() 2659 2660 config = defaults.copy() 2661 if overrides: 2662 config.update(overrides) 2663 2664 # If the user defined providers in salt cloud's main configuration file, we 2665 # need to take care for proper and expected format. 2666 if "providers" in config: 2667 # Keep a copy of the defined providers 2668 providers = config["providers"].copy() 2669 # Reset the providers dictionary 2670 config["providers"] = {} 2671 # Populate the providers dictionary 2672 for alias, details in providers.items(): 2673 if isinstance(details, list): 2674 for detail in details: 2675 if "driver" not in detail: 2676 raise salt.exceptions.SaltCloudConfigError( 2677 "The cloud provider alias '{}' has an entry " 2678 "missing the required setting of 'driver'.".format(alias) 2679 ) 2680 2681 driver = detail["driver"] 2682 2683 if ":" in driver: 2684 # Weird, but... 2685 alias, driver = driver.split(":") 2686 2687 if alias not in config["providers"]: 2688 config["providers"][alias] = {} 2689 2690 detail["provider"] = "{}:{}".format(alias, driver) 2691 config["providers"][alias][driver] = detail 2692 elif isinstance(details, dict): 2693 if "driver" not in details: 2694 raise salt.exceptions.SaltCloudConfigError( 2695 "The cloud provider alias '{}' has an entry " 2696 "missing the required setting of 'driver'".format(alias) 2697 ) 2698 2699 driver = details["driver"] 2700 2701 if ":" in driver: 2702 # Weird, but... 2703 alias, driver = driver.split(":") 2704 if alias not in config["providers"]: 2705 config["providers"][alias] = {} 2706 2707 details["provider"] = "{}:{}".format(alias, driver) 2708 config["providers"][alias][driver] = details 2709 2710 # Migrate old configuration 2711 config = old_to_new(config) 2712 2713 return config 2714 2715 2716def old_to_new(opts): 2717 providers = ( 2718 "AWS", 2719 "CLOUDSTACK", 2720 "DIGITALOCEAN", 2721 "EC2", 2722 "GOGRID", 2723 "IBMSCE", 2724 "JOYENT", 2725 "LINODE", 2726 "OPENSTACK", 2727 "PARALLELS", 2728 "RACKSPACE", 2729 "SALTIFY", 2730 ) 2731 2732 for provider in providers: 2733 2734 provider_config = {} 2735 for opt, val in opts.items(): 2736 if provider in opt: 2737 value = val 2738 name = opt.split(".", 1)[1] 2739 provider_config[name] = value 2740 2741 lprovider = provider.lower() 2742 if provider_config: 2743 provider_config["provider"] = lprovider 2744 opts.setdefault("providers", {}) 2745 # provider alias 2746 opts["providers"][lprovider] = {} 2747 # provider alias, provider driver 2748 opts["providers"][lprovider][lprovider] = provider_config 2749 return opts 2750 2751 2752def vm_profiles_config(path, providers, env_var="SALT_CLOUDVM_CONFIG", defaults=None): 2753 """ 2754 Read in the salt cloud VM config file 2755 """ 2756 if defaults is None: 2757 defaults = VM_CONFIG_DEFAULTS 2758 2759 overrides = salt.config.load_config( 2760 path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, "cloud.profiles") 2761 ) 2762 2763 default_include = overrides.get("default_include", defaults["default_include"]) 2764 include = overrides.get("include", []) 2765 2766 overrides.update(salt.config.include_config(default_include, path, verbose=False)) 2767 overrides.update(salt.config.include_config(include, path, verbose=True)) 2768 return apply_vm_profiles_config(providers, overrides, defaults) 2769 2770 2771def apply_vm_profiles_config(providers, overrides, defaults=None): 2772 if defaults is None: 2773 defaults = VM_CONFIG_DEFAULTS 2774 2775 config = defaults.copy() 2776 if overrides: 2777 config.update(overrides) 2778 2779 vms = {} 2780 2781 for key, val in config.items(): 2782 if key in ("conf_file", "include", "default_include", "user"): 2783 continue 2784 if not isinstance(val, dict): 2785 raise salt.exceptions.SaltCloudConfigError( 2786 "The VM profiles configuration found in '{0[conf_file]}' is " 2787 "not in the proper format".format(config) 2788 ) 2789 val["profile"] = key 2790 vms[key] = val 2791 2792 # Is any VM profile extending data!? 2793 for profile, details in vms.copy().items(): 2794 if "extends" not in details: 2795 if ":" in details["provider"]: 2796 alias, driver = details["provider"].split(":") 2797 if alias not in providers or driver not in providers[alias]: 2798 log.trace( 2799 "The profile '%s' is defining '%s' " 2800 "as the provider. Since there is no valid " 2801 "configuration for that provider, the profile will be " 2802 "removed from the available listing", 2803 profile, 2804 details["provider"], 2805 ) 2806 vms.pop(profile) 2807 continue 2808 2809 if "profiles" not in providers[alias][driver]: 2810 providers[alias][driver]["profiles"] = {} 2811 providers[alias][driver]["profiles"][profile] = details 2812 2813 if details["provider"] not in providers: 2814 log.trace( 2815 "The profile '%s' is defining '%s' as the " 2816 "provider. Since there is no valid configuration for " 2817 "that provider, the profile will be removed from the " 2818 "available listing", 2819 profile, 2820 details["provider"], 2821 ) 2822 vms.pop(profile) 2823 continue 2824 2825 driver = next(iter(list(providers[details["provider"]].keys()))) 2826 providers[details["provider"]][driver].setdefault("profiles", {}).update( 2827 {profile: details} 2828 ) 2829 details["provider"] = "{0[provider]}:{1}".format(details, driver) 2830 vms[profile] = details 2831 2832 continue 2833 2834 extends = details.pop("extends") 2835 if extends not in vms: 2836 log.error( 2837 "The '%s' profile is trying to extend data from '%s' " 2838 "though '%s' is not defined in the salt profiles loaded " 2839 "data. Not extending and removing from listing!", 2840 profile, 2841 extends, 2842 extends, 2843 ) 2844 vms.pop(profile) 2845 continue 2846 2847 extended = deepcopy(vms.get(extends)) 2848 extended.pop("profile") 2849 # Merge extended configuration with base profile 2850 extended = salt.utils.dictupdate.update(extended, details) 2851 2852 if ":" not in extended["provider"]: 2853 if extended["provider"] not in providers: 2854 log.trace( 2855 "The profile '%s' is defining '%s' as the " 2856 "provider. Since there is no valid configuration for " 2857 "that provider, the profile will be removed from the " 2858 "available listing", 2859 profile, 2860 extended["provider"], 2861 ) 2862 vms.pop(profile) 2863 continue 2864 2865 driver = next(iter(list(providers[extended["provider"]].keys()))) 2866 providers[extended["provider"]][driver].setdefault("profiles", {}).update( 2867 {profile: extended} 2868 ) 2869 2870 extended["provider"] = "{0[provider]}:{1}".format(extended, driver) 2871 else: 2872 alias, driver = extended["provider"].split(":") 2873 if alias not in providers or driver not in providers[alias]: 2874 log.trace( 2875 "The profile '%s' is defining '%s' as " 2876 "the provider. Since there is no valid configuration " 2877 "for that provider, the profile will be removed from " 2878 "the available listing", 2879 profile, 2880 extended["provider"], 2881 ) 2882 vms.pop(profile) 2883 continue 2884 2885 providers[alias][driver].setdefault("profiles", {}).update( 2886 {profile: extended} 2887 ) 2888 2889 # Update the profile's entry with the extended data 2890 vms[profile] = extended 2891 2892 return vms 2893 2894 2895def cloud_providers_config(path, env_var="SALT_CLOUD_PROVIDERS_CONFIG", defaults=None): 2896 """ 2897 Read in the salt cloud providers configuration file 2898 """ 2899 if defaults is None: 2900 defaults = PROVIDER_CONFIG_DEFAULTS 2901 2902 overrides = salt.config.load_config( 2903 path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers") 2904 ) 2905 2906 default_include = overrides.get("default_include", defaults["default_include"]) 2907 include = overrides.get("include", []) 2908 2909 overrides.update(salt.config.include_config(default_include, path, verbose=False)) 2910 overrides.update(salt.config.include_config(include, path, verbose=True)) 2911 return apply_cloud_providers_config(overrides, defaults) 2912 2913 2914def apply_cloud_providers_config(overrides, defaults=None): 2915 """ 2916 Apply the loaded cloud providers configuration. 2917 """ 2918 if defaults is None: 2919 defaults = PROVIDER_CONFIG_DEFAULTS 2920 2921 config = defaults.copy() 2922 if overrides: 2923 config.update(overrides) 2924 2925 # Is the user still using the old format in the new configuration file?! 2926 for name, settings in config.copy().items(): 2927 if "." in name: 2928 log.warning("Please switch to the new providers configuration syntax") 2929 2930 # Let's help out and migrate the data 2931 config = old_to_new(config) 2932 2933 # old_to_new will migrate the old data into the 'providers' key of 2934 # the config dictionary. Let's map it correctly 2935 for prov_name, prov_settings in config.pop("providers").items(): 2936 config[prov_name] = prov_settings 2937 break 2938 2939 providers = {} 2940 ext_count = 0 2941 for key, val in config.items(): 2942 if key in ("conf_file", "include", "default_include", "user"): 2943 continue 2944 2945 if not isinstance(val, (list, tuple)): 2946 val = [val] 2947 else: 2948 # Need to check for duplicate cloud provider entries per "alias" or 2949 # we won't be able to properly reference it. 2950 handled_providers = set() 2951 for details in val: 2952 if "driver" not in details: 2953 if "extends" not in details: 2954 log.error( 2955 "Please check your cloud providers configuration. " 2956 "There's no 'driver' nor 'extends' definition " 2957 "referenced." 2958 ) 2959 continue 2960 2961 if details["driver"] in handled_providers: 2962 log.error( 2963 "You can only have one entry per cloud provider. For " 2964 "example, if you have a cloud provider configuration " 2965 "section named, 'production', you can only have a " 2966 "single entry for EC2, Joyent, Openstack, and so " 2967 "forth." 2968 ) 2969 raise salt.exceptions.SaltCloudConfigError( 2970 "The cloud provider alias '{0}' has multiple entries " 2971 "for the '{1[driver]}' driver.".format(key, details) 2972 ) 2973 handled_providers.add(details["driver"]) 2974 2975 for entry in val: 2976 2977 if "driver" not in entry: 2978 entry["driver"] = "-only-extendable-{}".format(ext_count) 2979 ext_count += 1 2980 2981 if key not in providers: 2982 providers[key] = {} 2983 2984 provider = entry["driver"] 2985 if provider not in providers[key]: 2986 providers[key][provider] = entry 2987 2988 # Is any provider extending data!? 2989 while True: 2990 keep_looping = False 2991 for provider_alias, entries in providers.copy().items(): 2992 for driver, details in entries.items(): 2993 # Set a holder for the defined profiles 2994 providers[provider_alias][driver]["profiles"] = {} 2995 2996 if "extends" not in details: 2997 continue 2998 2999 extends = details.pop("extends") 3000 3001 if ":" in extends: 3002 alias, provider = extends.split(":") 3003 if alias not in providers: 3004 raise salt.exceptions.SaltCloudConfigError( 3005 "The '{0}' cloud provider entry in '{1}' is " 3006 "trying to extend data from '{2}' though " 3007 "'{2}' is not defined in the salt cloud " 3008 "providers loaded data.".format( 3009 details["driver"], provider_alias, alias 3010 ) 3011 ) 3012 3013 if provider not in providers.get(alias): 3014 raise salt.exceptions.SaltCloudConfigError( 3015 "The '{0}' cloud provider entry in '{1}' is " 3016 "trying to extend data from '{2}:{3}' though " 3017 "'{3}' is not defined in '{1}'".format( 3018 details["driver"], provider_alias, alias, provider 3019 ) 3020 ) 3021 details["extends"] = "{}:{}".format(alias, provider) 3022 # change provider details '-only-extendable-' to extended 3023 # provider name 3024 details["driver"] = provider 3025 elif providers.get(extends): 3026 raise salt.exceptions.SaltCloudConfigError( 3027 "The '{}' cloud provider entry in '{}' is " 3028 "trying to extend from '{}' and no provider was " 3029 "specified. Not extending!".format( 3030 details["driver"], provider_alias, extends 3031 ) 3032 ) 3033 elif extends not in providers: 3034 raise salt.exceptions.SaltCloudConfigError( 3035 "The '{0}' cloud provider entry in '{1}' is " 3036 "trying to extend data from '{2}' though '{2}' " 3037 "is not defined in the salt cloud providers loaded " 3038 "data.".format(details["driver"], provider_alias, extends) 3039 ) 3040 else: 3041 if driver in providers.get(extends): 3042 details["extends"] = "{}:{}".format(extends, driver) 3043 elif "-only-extendable-" in providers.get(extends): 3044 details["extends"] = "{}:{}".format( 3045 extends, "-only-extendable-{}".format(ext_count) 3046 ) 3047 else: 3048 # We're still not aware of what we're trying to extend 3049 # from. Let's try on next iteration 3050 details["extends"] = extends 3051 keep_looping = True 3052 if not keep_looping: 3053 break 3054 3055 while True: 3056 # Merge provided extends 3057 keep_looping = False 3058 for alias, entries in providers.copy().items(): 3059 for driver in list(entries.keys()): 3060 # Don't use iteritems, because the values of the dictionary will be changed 3061 details = entries[driver] 3062 3063 if "extends" not in details: 3064 # Extends resolved or non existing, continue! 3065 continue 3066 3067 if "extends" in details["extends"]: 3068 # Since there's a nested extends, resolve this one in the 3069 # next iteration 3070 keep_looping = True 3071 continue 3072 3073 # Let's get a reference to what we're supposed to extend 3074 extends = details.pop("extends") 3075 # Split the setting in (alias, driver) 3076 ext_alias, ext_driver = extends.split(":") 3077 # Grab a copy of what should be extended 3078 extended = providers.get(ext_alias).get(ext_driver).copy() 3079 # Merge the data to extend with the details 3080 extended = salt.utils.dictupdate.update(extended, details) 3081 # Update the providers dictionary with the merged data 3082 providers[alias][driver] = extended 3083 # Update name of the driver, now that it's populated with extended information 3084 if driver.startswith("-only-extendable-"): 3085 providers[alias][ext_driver] = providers[alias][driver] 3086 # Delete driver with old name to maintain dictionary size 3087 del providers[alias][driver] 3088 3089 if not keep_looping: 3090 break 3091 3092 # Now clean up any providers entry that was just used to be a data tree to 3093 # extend from 3094 for provider_alias, entries in providers.copy().items(): 3095 for driver, details in entries.copy().items(): 3096 if not driver.startswith("-only-extendable-"): 3097 continue 3098 3099 log.info( 3100 "There's at least one cloud driver under the '%s' " 3101 "cloud provider alias which does not have the required " 3102 "'driver' setting. Removing it from the available " 3103 "providers listing.", 3104 provider_alias, 3105 ) 3106 providers[provider_alias].pop(driver) 3107 3108 if not providers[provider_alias]: 3109 providers.pop(provider_alias) 3110 3111 return providers 3112 3113 3114def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): 3115 """ 3116 Search and return a setting in a known order: 3117 3118 1. In the virtual machine's configuration 3119 2. In the virtual machine's profile configuration 3120 3. In the virtual machine's provider configuration 3121 4. In the salt cloud configuration if global searching is enabled 3122 5. Return the provided default 3123 """ 3124 3125 # As a last resort, return the default 3126 value = default 3127 3128 if search_global is True and opts.get(name, None) is not None: 3129 # The setting name exists in the cloud(global) configuration 3130 value = deepcopy(opts[name]) 3131 3132 if vm_ and name: 3133 # Let's get the value from the profile, if present 3134 if "profile" in vm_ and vm_["profile"] is not None: 3135 if name in opts["profiles"][vm_["profile"]]: 3136 if isinstance(value, dict): 3137 value.update(opts["profiles"][vm_["profile"]][name].copy()) 3138 else: 3139 value = deepcopy(opts["profiles"][vm_["profile"]][name]) 3140 3141 # Let's get the value from the provider, if present. 3142 if ":" in vm_["driver"]: 3143 # The provider is defined as <provider-alias>:<driver-name> 3144 alias, driver = vm_["driver"].split(":") 3145 if alias in opts["providers"] and driver in opts["providers"][alias]: 3146 details = opts["providers"][alias][driver] 3147 if name in details: 3148 if isinstance(value, dict): 3149 value.update(details[name].copy()) 3150 else: 3151 value = deepcopy(details[name]) 3152 elif len(opts["providers"].get(vm_["driver"], ())) > 1: 3153 # The provider is NOT defined as <provider-alias>:<driver-name> 3154 # and there's more than one entry under the alias. 3155 # WARN the user!!!! 3156 log.error( 3157 "The '%s' cloud provider definition has more than one " 3158 "entry. Your VM configuration should be specifying the " 3159 "provider as 'driver: %s:<driver-engine>'. Since " 3160 "it's not, we're returning the first definition which " 3161 "might not be what you intended.", 3162 vm_["driver"], 3163 vm_["driver"], 3164 ) 3165 3166 if vm_["driver"] in opts["providers"]: 3167 # There's only one driver defined for this provider. This is safe. 3168 alias_defs = opts["providers"].get(vm_["driver"]) 3169 provider_driver_defs = alias_defs[next(iter(list(alias_defs.keys())))] 3170 if name in provider_driver_defs: 3171 # The setting name exists in the VM's provider configuration. 3172 # Return it! 3173 if isinstance(value, dict): 3174 value.update(provider_driver_defs[name].copy()) 3175 else: 3176 value = deepcopy(provider_driver_defs[name]) 3177 3178 if name and vm_ and name in vm_: 3179 # The setting name exists in VM configuration. 3180 if isinstance(vm_[name], types.GeneratorType): 3181 value = next(vm_[name], "") 3182 else: 3183 if isinstance(value, dict) and isinstance(vm_[name], dict): 3184 value.update(vm_[name].copy()) 3185 else: 3186 value = deepcopy(vm_[name]) 3187 3188 return value 3189 3190 3191def is_provider_configured( 3192 opts, provider, required_keys=(), log_message=True, aliases=() 3193): 3194 """ 3195 Check and return the first matching and fully configured cloud provider 3196 configuration. 3197 """ 3198 if ":" in provider: 3199 alias, driver = provider.split(":") 3200 if alias not in opts["providers"]: 3201 return False 3202 if driver not in opts["providers"][alias]: 3203 return False 3204 for key in required_keys: 3205 if opts["providers"][alias][driver].get(key, None) is None: 3206 if log_message is True: 3207 # There's at least one require configuration key which is not 3208 # set. 3209 log.warning( 3210 "The required '%s' configuration setting is missing " 3211 "from the '%s' driver, which is configured under the " 3212 "'%s' alias.", 3213 key, 3214 provider, 3215 alias, 3216 ) 3217 return False 3218 # If we reached this far, there's a properly configured provider. 3219 # Return it! 3220 return opts["providers"][alias][driver] 3221 3222 for alias, drivers in opts["providers"].items(): 3223 for driver, provider_details in drivers.items(): 3224 if driver != provider and driver not in aliases: 3225 continue 3226 3227 # If we reached this far, we have a matching provider, let's see if 3228 # all required configuration keys are present and not None. 3229 skip_provider = False 3230 for key in required_keys: 3231 if provider_details.get(key, None) is None: 3232 if log_message is True: 3233 # This provider does not include all necessary keys, 3234 # continue to next one. 3235 log.warning( 3236 "The required '%s' configuration setting is " 3237 "missing from the '%s' driver, which is configured " 3238 "under the '%s' alias.", 3239 key, 3240 provider, 3241 alias, 3242 ) 3243 skip_provider = True 3244 break 3245 3246 if skip_provider: 3247 continue 3248 3249 # If we reached this far, the provider included all required keys 3250 return provider_details 3251 3252 # If we reached this point, the provider is not configured. 3253 return False 3254 3255 3256def is_profile_configured(opts, provider, profile_name, vm_=None): 3257 """ 3258 Check if the requested profile contains the minimum required parameters for 3259 a profile. 3260 3261 Required parameters include image and provider for all drivers, while some 3262 drivers also require size keys. 3263 3264 .. versionadded:: 2015.8.0 3265 """ 3266 # Standard dict keys required by all drivers. 3267 required_keys = ["provider"] 3268 alias, driver = provider.split(":") 3269 3270 # Most drivers need an image to be specified, but some do not. 3271 non_image_drivers = [ 3272 "nova", 3273 "virtualbox", 3274 "libvirt", 3275 "softlayer", 3276 "oneandone", 3277 "profitbricks", 3278 ] 3279 3280 # Most drivers need a size, but some do not. 3281 non_size_drivers = [ 3282 "opennebula", 3283 "parallels", 3284 "proxmox", 3285 "scaleway", 3286 "softlayer", 3287 "softlayer_hw", 3288 "vmware", 3289 "vsphere", 3290 "virtualbox", 3291 "libvirt", 3292 "oneandone", 3293 "profitbricks", 3294 ] 3295 3296 provider_key = opts["providers"][alias][driver] 3297 profile_key = opts["providers"][alias][driver]["profiles"][profile_name] 3298 3299 # If cloning on Linode, size and image are not necessary. 3300 # They are obtained from the to-be-cloned VM. 3301 if driver == "linode" and profile_key.get("clonefrom", False): 3302 non_image_drivers.append("linode") 3303 non_size_drivers.append("linode") 3304 elif driver == "gce" and "sourceImage" in str(vm_.get("ex_disks_gce_struct")): 3305 non_image_drivers.append("gce") 3306 3307 # If cloning on VMware, specifying image is not necessary. 3308 if driver == "vmware" and "image" not in list(profile_key.keys()): 3309 non_image_drivers.append("vmware") 3310 3311 if driver not in non_image_drivers: 3312 required_keys.append("image") 3313 if driver == "vmware": 3314 required_keys.append("datastore") 3315 elif driver in ["linode", "virtualbox"]: 3316 required_keys.append("clonefrom") 3317 elif driver == "nova": 3318 nova_image_keys = [ 3319 "image", 3320 "block_device_mapping", 3321 "block_device", 3322 "boot_volume", 3323 ] 3324 if not any([key in provider_key for key in nova_image_keys]) and not any( 3325 [key in profile_key for key in nova_image_keys] 3326 ): 3327 required_keys.extend(nova_image_keys) 3328 3329 if driver not in non_size_drivers: 3330 required_keys.append("size") 3331 3332 # Check if required fields are supplied in the provider config. If they 3333 # are present, remove it from the required_keys list. 3334 for item in list(required_keys): 3335 if item in provider_key: 3336 required_keys.remove(item) 3337 3338 # If a vm_ dict was passed in, use that information to get any other configs 3339 # that we might have missed thus far, such as a option provided in a map file. 3340 if vm_: 3341 for item in list(required_keys): 3342 if item in vm_: 3343 required_keys.remove(item) 3344 3345 # Check for remaining required parameters in the profile config. 3346 for item in required_keys: 3347 if profile_key.get(item, None) is None: 3348 # There's at least one required configuration item which is not set. 3349 log.error( 3350 "The required '%s' configuration setting is missing from " 3351 "the '%s' profile, which is configured under the '%s' alias.", 3352 item, 3353 profile_name, 3354 alias, 3355 ) 3356 return False 3357 3358 return True 3359 3360 3361def check_driver_dependencies(driver, dependencies): 3362 """ 3363 Check if the driver's dependencies are available. 3364 3365 .. versionadded:: 2015.8.0 3366 3367 driver 3368 The name of the driver. 3369 3370 dependencies 3371 The dictionary of dependencies to check. 3372 """ 3373 ret = True 3374 for key, value in dependencies.items(): 3375 if value is False: 3376 log.warning( 3377 "Missing dependency: '%s'. The %s driver requires " 3378 "'%s' to be installed.", 3379 key, 3380 driver, 3381 key, 3382 ) 3383 ret = False 3384 3385 return ret 3386 3387 3388# <---- Salt Cloud Configuration Functions ----------------------------------- 3389 3390 3391def _cache_id(minion_id, cache_file): 3392 """ 3393 Helper function, writes minion id to a cache file. 3394 """ 3395 path = os.path.dirname(cache_file) 3396 try: 3397 if not os.path.isdir(path): 3398 os.makedirs(path) 3399 except OSError as exc: 3400 # Handle race condition where dir is created after os.path.isdir check 3401 if os.path.isdir(path): 3402 pass 3403 else: 3404 log.error("Failed to create dirs to minion_id file: %s", exc) 3405 3406 try: 3407 with salt.utils.files.fopen(cache_file, "w") as idf: 3408 idf.write(minion_id) 3409 except OSError as exc: 3410 log.error("Could not cache minion ID: %s", exc) 3411 3412 3413def call_id_function(opts): 3414 """ 3415 Evaluate the function that determines the ID if the 'id_function' 3416 option is set and return the result 3417 """ 3418 if opts.get("id"): 3419 return opts["id"] 3420 3421 # Import 'salt.loader' here to avoid a circular dependency 3422 import salt.loader as loader 3423 3424 if isinstance(opts["id_function"], str): 3425 mod_fun = opts["id_function"] 3426 fun_kwargs = {} 3427 elif isinstance(opts["id_function"], dict): 3428 mod_fun, fun_kwargs = next(iter(opts["id_function"].items())) 3429 if fun_kwargs is None: 3430 fun_kwargs = {} 3431 else: 3432 log.error("'id_function' option is neither a string nor a dictionary") 3433 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 3434 3435 # split module and function and try loading the module 3436 mod, fun = mod_fun.split(".") 3437 if not opts.get("grains"): 3438 # Get grains for use by the module 3439 opts["grains"] = loader.grains(opts) 3440 3441 try: 3442 id_mod = loader.raw_mod(opts, mod, fun) 3443 if not id_mod: 3444 raise KeyError 3445 # we take whatever the module returns as the minion ID 3446 newid = id_mod[mod_fun](**fun_kwargs) 3447 if not isinstance(newid, str) or not newid: 3448 log.error( 3449 'Function %s returned value "%s" of type %s instead of string', 3450 mod_fun, 3451 newid, 3452 type(newid), 3453 ) 3454 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 3455 log.info("Evaluated minion ID from module: %s %s", mod_fun, newid) 3456 return newid 3457 except TypeError: 3458 log.error( 3459 "Function arguments %s are incorrect for function %s", fun_kwargs, mod_fun 3460 ) 3461 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 3462 except KeyError: 3463 log.error("Failed to load module %s", mod_fun) 3464 sys.exit(salt.defaults.exitcodes.EX_GENERIC) 3465 3466 3467def remove_domain_from_fqdn(opts, newid): 3468 """ 3469 Depending on the values of `minion_id_remove_domain`, 3470 remove all domains or a single domain from a FQDN, effectivly generating a hostname. 3471 """ 3472 opt_domain = opts.get("minion_id_remove_domain") 3473 if opt_domain is True: 3474 if "." in newid: 3475 # Remove any domain 3476 newid, xdomain = newid.split(".", 1) 3477 log.debug("Removed any domain (%s) from minion id.", xdomain) 3478 else: 3479 # Must be string type 3480 if newid.upper().endswith("." + opt_domain.upper()): 3481 # Remove single domain 3482 newid = newid[: -len("." + opt_domain)] 3483 log.debug("Removed single domain %s from minion id.", opt_domain) 3484 return newid 3485 3486 3487def get_id(opts, cache_minion_id=False): 3488 """ 3489 Guess the id of the minion. 3490 3491 If CONFIG_DIR/minion_id exists, use the cached minion ID from that file. 3492 If no minion id is configured, use multiple sources to find a FQDN. 3493 If no FQDN is found you may get an ip address. 3494 3495 Returns two values: the detected ID, and a boolean value noting whether or 3496 not an IP address is being used for the ID. 3497 """ 3498 if opts["root_dir"] is None: 3499 root_dir = salt.syspaths.ROOT_DIR 3500 else: 3501 root_dir = opts["root_dir"] 3502 3503 config_dir = salt.syspaths.CONFIG_DIR 3504 if config_dir.startswith(salt.syspaths.ROOT_DIR): 3505 config_dir = config_dir.split(salt.syspaths.ROOT_DIR, 1)[-1] 3506 3507 # Check for cached minion ID 3508 id_cache = os.path.join(root_dir, config_dir.lstrip(os.path.sep), "minion_id") 3509 3510 if opts.get("minion_id_caching", True): 3511 try: 3512 with salt.utils.files.fopen(id_cache) as idf: 3513 name = salt.utils.stringutils.to_unicode(idf.readline().strip()) 3514 bname = salt.utils.stringutils.to_bytes(name) 3515 if bname.startswith(codecs.BOM): # Remove BOM if exists 3516 name = salt.utils.stringutils.to_str( 3517 bname.replace(codecs.BOM, "", 1) 3518 ) 3519 if name and name != "localhost": 3520 log.debug("Using cached minion ID from %s: %s", id_cache, name) 3521 return name, False 3522 except OSError: 3523 pass 3524 if "__role" in opts and opts.get("__role") == "minion": 3525 log.debug( 3526 "Guessing ID. The id can be explicitly set in %s", 3527 os.path.join(salt.syspaths.CONFIG_DIR, "minion"), 3528 ) 3529 3530 if opts.get("id_function"): 3531 newid = call_id_function(opts) 3532 else: 3533 newid = salt.utils.network.generate_minion_id() 3534 3535 if opts.get("minion_id_lowercase"): 3536 newid = newid.lower() 3537 log.debug("Changed minion id %s to lowercase.", newid) 3538 3539 # Optionally remove one or many domains in a generated minion id 3540 if opts.get("minion_id_remove_domain"): 3541 newid = remove_domain_from_fqdn(opts, newid) 3542 3543 if "__role" in opts and opts.get("__role") == "minion": 3544 if opts.get("id_function"): 3545 log.debug( 3546 "Found minion id from external function %s: %s", 3547 opts["id_function"], 3548 newid, 3549 ) 3550 else: 3551 log.debug("Found minion id from generate_minion_id(): %s", newid) 3552 if cache_minion_id and opts.get("minion_id_caching", True): 3553 _cache_id(newid, id_cache) 3554 is_ipv4 = salt.utils.network.is_ipv4(newid) 3555 return newid, is_ipv4 3556 3557 3558def _update_ssl_config(opts): 3559 """ 3560 Resolves string names to integer constant in ssl configuration. 3561 """ 3562 if opts["ssl"] in (None, False): 3563 opts["ssl"] = None 3564 return 3565 if opts["ssl"] is True: 3566 opts["ssl"] = {} 3567 return 3568 import ssl 3569 3570 for key, prefix in (("cert_reqs", "CERT_"), ("ssl_version", "PROTOCOL_")): 3571 val = opts["ssl"].get(key) 3572 if val is None: 3573 continue 3574 if ( 3575 not isinstance(val, str) 3576 or not val.startswith(prefix) 3577 or not hasattr(ssl, val) 3578 ): 3579 message = "SSL option '{}' must be set to one of the following values: '{}'.".format( 3580 key, 3581 "', '".join([val for val in dir(ssl) if val.startswith(prefix)]), 3582 ) 3583 log.error(message) 3584 raise salt.exceptions.SaltConfigurationError(message) 3585 opts["ssl"][key] = getattr(ssl, val) 3586 3587 3588def _adjust_log_file_override(overrides, default_log_file): 3589 """ 3590 Adjusts the log_file based on the log_dir override 3591 """ 3592 if overrides.get("log_dir"): 3593 # Adjust log_file if a log_dir override is introduced 3594 if overrides.get("log_file"): 3595 if not os.path.isabs(overrides["log_file"]): 3596 # Prepend log_dir if log_file is relative 3597 overrides["log_file"] = os.path.join( 3598 overrides["log_dir"], overrides["log_file"] 3599 ) 3600 else: 3601 # Create the log_file override 3602 overrides["log_file"] = os.path.join( 3603 overrides["log_dir"], os.path.basename(default_log_file) 3604 ) 3605 3606 3607def apply_minion_config( 3608 overrides=None, defaults=None, cache_minion_id=False, minion_id=None 3609): 3610 """ 3611 Returns minion configurations dict. 3612 """ 3613 if defaults is None: 3614 defaults = DEFAULT_MINION_OPTS.copy() 3615 if overrides is None: 3616 overrides = {} 3617 3618 opts = defaults.copy() 3619 opts["__role"] = "minion" 3620 _adjust_log_file_override(overrides, defaults["log_file"]) 3621 if overrides: 3622 opts.update(overrides) 3623 3624 if "environment" in opts: 3625 if opts["saltenv"] is not None: 3626 log.warning( 3627 "The 'saltenv' and 'environment' minion config options " 3628 "cannot both be used. Ignoring 'environment' in favor of " 3629 "'saltenv'." 3630 ) 3631 # Set environment to saltenv in case someone's custom module is 3632 # refrencing __opts__['environment'] 3633 opts["environment"] = opts["saltenv"] 3634 else: 3635 log.warning( 3636 "The 'environment' minion config option has been renamed " 3637 "to 'saltenv'. Using %s as the 'saltenv' config value.", 3638 opts["environment"], 3639 ) 3640 opts["saltenv"] = opts["environment"] 3641 3642 for idx, val in enumerate(opts["fileserver_backend"]): 3643 if val in ("git", "hg", "svn", "minion"): 3644 new_val = val + "fs" 3645 log.debug( 3646 "Changed %s to %s in minion opts' fileserver_backend list", val, new_val 3647 ) 3648 opts["fileserver_backend"][idx] = new_val 3649 3650 opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0])) 3651 3652 # No ID provided. Will getfqdn save us? 3653 using_ip_for_id = False 3654 if not opts.get("id"): 3655 if minion_id: 3656 opts["id"] = minion_id 3657 else: 3658 opts["id"], using_ip_for_id = get_id(opts, cache_minion_id=cache_minion_id) 3659 3660 # it does not make sense to append a domain to an IP based id 3661 if not using_ip_for_id and "append_domain" in opts: 3662 opts["id"] = _append_domain(opts) 3663 3664 for directory in opts.get("append_minionid_config_dirs", []): 3665 if directory in ("pki_dir", "cachedir", "extension_modules"): 3666 newdirectory = os.path.join(opts[directory], opts["id"]) 3667 opts[directory] = newdirectory 3668 elif directory == "default_include" and directory in opts: 3669 include_dir = os.path.dirname(opts[directory]) 3670 new_include_dir = os.path.join( 3671 include_dir, opts["id"], os.path.basename(opts[directory]) 3672 ) 3673 opts[directory] = new_include_dir 3674 3675 # pidfile can be in the list of append_minionid_config_dirs, but pidfile 3676 # is the actual path with the filename, not a directory. 3677 if "pidfile" in opts.get("append_minionid_config_dirs", []): 3678 newpath_list = os.path.split(opts["pidfile"]) 3679 opts["pidfile"] = os.path.join( 3680 newpath_list[0], "salt", opts["id"], newpath_list[1] 3681 ) 3682 3683 if len(opts["sock_dir"]) > len(opts["cachedir"]) + 10: 3684 opts["sock_dir"] = os.path.join(opts["cachedir"], ".salt-unix") 3685 3686 # Enabling open mode requires that the value be set to True, and 3687 # nothing else! 3688 opts["open_mode"] = opts["open_mode"] is True 3689 opts["file_roots"] = _validate_file_roots(opts["file_roots"]) 3690 opts["pillar_roots"] = _validate_pillar_roots(opts["pillar_roots"]) 3691 # Make sure ext_mods gets set if it is an untrue value 3692 # (here to catch older bad configs) 3693 opts["extension_modules"] = opts.get("extension_modules") or os.path.join( 3694 opts["cachedir"], "extmods" 3695 ) 3696 # Set up the utils_dirs location from the extension_modules location 3697 opts["utils_dirs"] = opts.get("utils_dirs") or [ 3698 os.path.join(opts["extension_modules"], "utils") 3699 ] 3700 3701 # Insert all 'utils_dirs' directories to the system path 3702 insert_system_path(opts, opts["utils_dirs"]) 3703 3704 # Prepend root_dir to other paths 3705 prepend_root_dirs = [ 3706 "pki_dir", 3707 "cachedir", 3708 "sock_dir", 3709 "extension_modules", 3710 "pidfile", 3711 ] 3712 3713 # These can be set to syslog, so, not actual paths on the system 3714 for config_key in ("log_file", "key_logfile"): 3715 if urllib.parse.urlparse(opts.get(config_key, "")).scheme == "": 3716 prepend_root_dirs.append(config_key) 3717 3718 prepend_root_dir(opts, prepend_root_dirs) 3719 3720 # if there is no beacons option yet, add an empty beacons dict 3721 if "beacons" not in opts: 3722 opts["beacons"] = {} 3723 3724 if overrides.get("ipc_write_buffer", "") == "dynamic": 3725 opts["ipc_write_buffer"] = _DFLT_IPC_WBUFFER 3726 if "ipc_write_buffer" not in overrides: 3727 opts["ipc_write_buffer"] = 0 3728 3729 # Make sure hash_type is lowercase 3730 opts["hash_type"] = opts["hash_type"].lower() 3731 3732 # Check and update TLS/SSL configuration 3733 _update_ssl_config(opts) 3734 _update_discovery_config(opts) 3735 3736 return opts 3737 3738 3739def _update_discovery_config(opts): 3740 """ 3741 Update discovery config for all instances. 3742 3743 :param opts: 3744 :return: 3745 """ 3746 if opts.get("discovery") not in (None, False): 3747 if opts["discovery"] is True: 3748 opts["discovery"] = {} 3749 discovery_config = { 3750 "attempts": 3, 3751 "pause": 5, 3752 "port": 4520, 3753 "match": "any", 3754 "mapping": {}, 3755 } 3756 for key in opts["discovery"]: 3757 if key not in discovery_config: 3758 raise salt.exceptions.SaltConfigurationError( 3759 "Unknown discovery option: {}".format(key) 3760 ) 3761 if opts.get("__role") != "minion": 3762 for key in ["attempts", "pause", "match"]: 3763 del discovery_config[key] 3764 opts["discovery"] = salt.utils.dictupdate.update( 3765 discovery_config, opts["discovery"], True, True 3766 ) 3767 3768 3769def master_config( 3770 path, env_var="SALT_MASTER_CONFIG", defaults=None, exit_on_config_errors=False 3771): 3772 """ 3773 Reads in the master configuration file and sets up default options 3774 3775 This is useful for running the actual master daemon. For running 3776 Master-side client interfaces that need the master opts see 3777 :py:func:`salt.client.client_config`. 3778 """ 3779 if defaults is None: 3780 defaults = DEFAULT_MASTER_OPTS.copy() 3781 3782 if not os.environ.get(env_var, None): 3783 # No valid setting was given using the configuration variable. 3784 # Lets see is SALT_CONFIG_DIR is of any use 3785 salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) 3786 if salt_config_dir: 3787 env_config_file_path = os.path.join(salt_config_dir, "master") 3788 if salt_config_dir and os.path.isfile(env_config_file_path): 3789 # We can get a configuration file using SALT_CONFIG_DIR, let's 3790 # update the environment with this information 3791 os.environ[env_var] = env_config_file_path 3792 3793 overrides = load_config(path, env_var, DEFAULT_MASTER_OPTS["conf_file"]) 3794 default_include = overrides.get("default_include", defaults["default_include"]) 3795 include = overrides.get("include", []) 3796 3797 overrides.update( 3798 include_config( 3799 default_include, 3800 path, 3801 verbose=False, 3802 exit_on_config_errors=exit_on_config_errors, 3803 ) 3804 ) 3805 overrides.update( 3806 include_config( 3807 include, path, verbose=True, exit_on_config_errors=exit_on_config_errors 3808 ) 3809 ) 3810 opts = apply_master_config(overrides, defaults) 3811 _validate_ssh_minion_opts(opts) 3812 _validate_opts(opts) 3813 # If 'nodegroups:' is uncommented in the master config file, and there are 3814 # no nodegroups defined, opts['nodegroups'] will be None. Fix this by 3815 # reverting this value to the default, as if 'nodegroups:' was commented 3816 # out or not present. 3817 if opts.get("nodegroups") is None: 3818 opts["nodegroups"] = DEFAULT_MASTER_OPTS.get("nodegroups", {}) 3819 if salt.utils.data.is_dictlist(opts["nodegroups"]): 3820 opts["nodegroups"] = salt.utils.data.repack_dictlist(opts["nodegroups"]) 3821 apply_sdb(opts) 3822 return opts 3823 3824 3825def apply_master_config(overrides=None, defaults=None): 3826 """ 3827 Returns master configurations dict. 3828 """ 3829 if defaults is None: 3830 defaults = DEFAULT_MASTER_OPTS.copy() 3831 if overrides is None: 3832 overrides = {} 3833 3834 opts = defaults.copy() 3835 opts["__role"] = "master" 3836 _adjust_log_file_override(overrides, defaults["log_file"]) 3837 if overrides: 3838 opts.update(overrides) 3839 3840 opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0])) 3841 3842 if "environment" in opts: 3843 if opts["saltenv"] is not None: 3844 log.warning( 3845 "The 'saltenv' and 'environment' master config options " 3846 "cannot both be used. Ignoring 'environment' in favor of " 3847 "'saltenv'." 3848 ) 3849 # Set environment to saltenv in case someone's custom runner is 3850 # refrencing __opts__['environment'] 3851 opts["environment"] = opts["saltenv"] 3852 else: 3853 log.warning( 3854 "The 'environment' master config option has been renamed " 3855 "to 'saltenv'. Using %s as the 'saltenv' config value.", 3856 opts["environment"], 3857 ) 3858 opts["saltenv"] = opts["environment"] 3859 3860 for idx, val in enumerate(opts["fileserver_backend"]): 3861 if val in ("git", "hg", "svn", "minion"): 3862 new_val = val + "fs" 3863 log.debug( 3864 "Changed %s to %s in master opts' fileserver_backend list", val, new_val 3865 ) 3866 opts["fileserver_backend"][idx] = new_val 3867 3868 if len(opts["sock_dir"]) > len(opts["cachedir"]) + 10: 3869 opts["sock_dir"] = os.path.join(opts["cachedir"], ".salt-unix") 3870 3871 opts["token_dir"] = os.path.join(opts["cachedir"], "tokens") 3872 opts["syndic_dir"] = os.path.join(opts["cachedir"], "syndics") 3873 # Make sure ext_mods gets set if it is an untrue value 3874 # (here to catch older bad configs) 3875 opts["extension_modules"] = opts.get("extension_modules") or os.path.join( 3876 opts["cachedir"], "extmods" 3877 ) 3878 # Set up the utils_dirs location from the extension_modules location 3879 opts["utils_dirs"] = opts.get("utils_dirs") or [ 3880 os.path.join(opts["extension_modules"], "utils") 3881 ] 3882 3883 # Insert all 'utils_dirs' directories to the system path 3884 insert_system_path(opts, opts["utils_dirs"]) 3885 3886 if overrides.get("ipc_write_buffer", "") == "dynamic": 3887 opts["ipc_write_buffer"] = _DFLT_IPC_WBUFFER 3888 if "ipc_write_buffer" not in overrides: 3889 opts["ipc_write_buffer"] = 0 3890 using_ip_for_id = False 3891 append_master = False 3892 if not opts.get("id"): 3893 opts["id"], using_ip_for_id = get_id(opts, cache_minion_id=None) 3894 append_master = True 3895 3896 # it does not make sense to append a domain to an IP based id 3897 if not using_ip_for_id and "append_domain" in opts: 3898 opts["id"] = _append_domain(opts) 3899 if append_master: 3900 opts["id"] += "_master" 3901 3902 # Prepend root_dir to other paths 3903 prepend_root_dirs = [ 3904 "pki_dir", 3905 "cachedir", 3906 "pidfile", 3907 "sock_dir", 3908 "extension_modules", 3909 "autosign_file", 3910 "autoreject_file", 3911 "token_dir", 3912 "syndic_dir", 3913 "sqlite_queue_dir", 3914 "autosign_grains_dir", 3915 ] 3916 3917 # These can be set to syslog, so, not actual paths on the system 3918 for config_key in ("log_file", "key_logfile", "ssh_log_file"): 3919 log_setting = opts.get(config_key, "") 3920 if log_setting is None: 3921 continue 3922 3923 if urllib.parse.urlparse(log_setting).scheme == "": 3924 prepend_root_dirs.append(config_key) 3925 3926 prepend_root_dir(opts, prepend_root_dirs) 3927 3928 # Enabling open mode requires that the value be set to True, and 3929 # nothing else! 3930 opts["open_mode"] = opts["open_mode"] is True 3931 opts["auto_accept"] = opts["auto_accept"] is True 3932 opts["file_roots"] = _validate_file_roots(opts["file_roots"]) 3933 opts["pillar_roots"] = _validate_file_roots(opts["pillar_roots"]) 3934 3935 if opts["file_ignore_regex"]: 3936 # If file_ignore_regex was given, make sure it's wrapped in a list. 3937 # Only keep valid regex entries for improved performance later on. 3938 if isinstance(opts["file_ignore_regex"], str): 3939 ignore_regex = [opts["file_ignore_regex"]] 3940 elif isinstance(opts["file_ignore_regex"], list): 3941 ignore_regex = opts["file_ignore_regex"] 3942 3943 opts["file_ignore_regex"] = [] 3944 for regex in ignore_regex: 3945 try: 3946 # Can't store compiled regex itself in opts (breaks 3947 # serialization) 3948 re.compile(regex) 3949 opts["file_ignore_regex"].append(regex) 3950 except Exception: # pylint: disable=broad-except 3951 log.warning("Unable to parse file_ignore_regex. Skipping: %s", regex) 3952 3953 if opts["file_ignore_glob"]: 3954 # If file_ignore_glob was given, make sure it's wrapped in a list. 3955 if isinstance(opts["file_ignore_glob"], str): 3956 opts["file_ignore_glob"] = [opts["file_ignore_glob"]] 3957 3958 # Let's make sure `worker_threads` does not drop below 3 which has proven 3959 # to make `salt.modules.publish` not work under the test-suite. 3960 if opts["worker_threads"] < 3 and opts.get("peer", None): 3961 log.warning( 3962 "The 'worker_threads' setting in '%s' cannot be lower than " 3963 "3. Resetting it to the default value of 3.", 3964 opts["conf_file"], 3965 ) 3966 opts["worker_threads"] = 3 3967 3968 opts.setdefault("pillar_source_merging_strategy", "smart") 3969 3970 # Make sure hash_type is lowercase 3971 opts["hash_type"] = opts["hash_type"].lower() 3972 3973 # Check and update TLS/SSL configuration 3974 _update_ssl_config(opts) 3975 _update_discovery_config(opts) 3976 3977 return opts 3978 3979 3980def client_config(path, env_var="SALT_CLIENT_CONFIG", defaults=None): 3981 """ 3982 Load Master configuration data 3983 3984 Usage: 3985 3986 .. code-block:: python 3987 3988 import salt.config 3989 master_opts = salt.config.client_config('/etc/salt/master') 3990 3991 Returns a dictionary of the Salt Master configuration file with necessary 3992 options needed to communicate with a locally-running Salt Master daemon. 3993 This function searches for client specific configurations and adds them to 3994 the data from the master configuration. 3995 3996 This is useful for master-side operations like 3997 :py:class:`~salt.client.LocalClient`. 3998 """ 3999 if defaults is None: 4000 defaults = DEFAULT_MASTER_OPTS.copy() 4001 4002 xdg_dir = salt.utils.xdg.xdg_config_dir() 4003 if os.path.isdir(xdg_dir): 4004 client_config_dir = xdg_dir 4005 saltrc_config_file = "saltrc" 4006 else: 4007 client_config_dir = os.path.expanduser("~") 4008 saltrc_config_file = ".saltrc" 4009 4010 # Get the token file path from the provided defaults. If not found, specify 4011 # our own, sane, default 4012 opts = { 4013 "token_file": defaults.get( 4014 "token_file", os.path.join(client_config_dir, "salt_token") 4015 ) 4016 } 4017 # Update options with the master configuration, either from the provided 4018 # path, salt's defaults or provided defaults 4019 opts.update(master_config(path, defaults=defaults)) 4020 # Update with the users salt dot file or with the environment variable 4021 saltrc_config = os.path.join(client_config_dir, saltrc_config_file) 4022 opts.update(load_config(saltrc_config, env_var, saltrc_config)) 4023 # Make sure we have a proper and absolute path to the token file 4024 if "token_file" in opts: 4025 opts["token_file"] = os.path.abspath(os.path.expanduser(opts["token_file"])) 4026 # If the token file exists, read and store the contained token 4027 if os.path.isfile(opts["token_file"]): 4028 # Make sure token is still valid 4029 expire = opts.get("token_expire", 43200) 4030 if os.stat(opts["token_file"]).st_mtime + expire > time.mktime( 4031 time.localtime() 4032 ): 4033 with salt.utils.files.fopen(opts["token_file"]) as fp_: 4034 opts["token"] = fp_.read().strip() 4035 # On some platforms, like OpenBSD, 0.0.0.0 won't catch a master running on localhost 4036 if opts["interface"] == "0.0.0.0": 4037 opts["interface"] = "127.0.0.1" 4038 4039 # Make sure the master_uri is set 4040 if "master_uri" not in opts: 4041 opts["master_uri"] = "tcp://{ip}:{port}".format( 4042 ip=salt.utils.zeromq.ip_bracket(opts["interface"]), port=opts["ret_port"] 4043 ) 4044 4045 # Return the client options 4046 _validate_opts(opts) 4047 return opts 4048 4049 4050def api_config(path): 4051 """ 4052 Read in the Salt Master config file and add additional configs that 4053 need to be stubbed out for salt-api 4054 """ 4055 # Let's grab a copy of salt-api's required defaults 4056 opts = DEFAULT_API_OPTS.copy() 4057 4058 # Let's override them with salt's master opts 4059 opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS.copy())) 4060 4061 # Let's set the pidfile and log_file values in opts to api settings 4062 opts.update( 4063 { 4064 "pidfile": opts.get("api_pidfile", DEFAULT_API_OPTS["api_pidfile"]), 4065 "log_file": opts.get("api_logfile", DEFAULT_API_OPTS["api_logfile"]), 4066 } 4067 ) 4068 4069 prepend_root_dir(opts, ["api_pidfile", "api_logfile", "log_file", "pidfile"]) 4070 return opts 4071 4072 4073def spm_config(path): 4074 """ 4075 Read in the salt master config file and add additional configs that 4076 need to be stubbed out for spm 4077 4078 .. versionadded:: 2015.8.0 4079 """ 4080 # Let's grab a copy of salt's master default opts 4081 defaults = DEFAULT_MASTER_OPTS.copy() 4082 # Let's override them with spm's required defaults 4083 defaults.update(DEFAULT_SPM_OPTS) 4084 4085 overrides = load_config(path, "SPM_CONFIG", DEFAULT_SPM_OPTS["spm_conf_file"]) 4086 default_include = overrides.get( 4087 "spm_default_include", defaults["spm_default_include"] 4088 ) 4089 include = overrides.get("include", []) 4090 4091 overrides.update(include_config(default_include, path, verbose=False)) 4092 overrides.update(include_config(include, path, verbose=True)) 4093 defaults = apply_master_config(overrides, defaults) 4094 defaults = apply_spm_config(overrides, defaults) 4095 return client_config(path, env_var="SPM_CONFIG", defaults=defaults) 4096 4097 4098def apply_spm_config(overrides, defaults): 4099 """ 4100 Returns the spm configurations dict. 4101 4102 .. versionadded:: 2015.8.1 4103 """ 4104 opts = defaults.copy() 4105 _adjust_log_file_override(overrides, defaults["log_file"]) 4106 if overrides: 4107 opts.update(overrides) 4108 4109 # Prepend root_dir to other paths 4110 prepend_root_dirs = [ 4111 "formula_path", 4112 "pillar_path", 4113 "reactor_path", 4114 "spm_cache_dir", 4115 "spm_build_dir", 4116 ] 4117 4118 # These can be set to syslog, so, not actual paths on the system 4119 for config_key in ("spm_logfile",): 4120 log_setting = opts.get(config_key, "") 4121 if log_setting is None: 4122 continue 4123 4124 if urllib.parse.urlparse(log_setting).scheme == "": 4125 prepend_root_dirs.append(config_key) 4126 4127 prepend_root_dir(opts, prepend_root_dirs) 4128 return opts 4129