%PDF- %PDF-
Direktori : /proc/227033/root/lib/python2.7/site-packages/salt/cli/ |
Current File : //proc/227033/root/lib/python2.7/site-packages/salt/cli/daemons.py |
# coding: utf-8 -*- ''' Make me some salt! ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os import warnings from salt.utils.verify import verify_log # All salt related deprecation warnings should be shown once each! warnings.filterwarnings( 'once', # Show once '', # No deprecation message match DeprecationWarning, # This filter is for DeprecationWarnings r'^(salt|salt\.(.*))$' # Match module(s) 'salt' and 'salt.<whatever>' ) # While we are supporting Python2.6, hide nested with-statements warnings warnings.filterwarnings( 'ignore', 'With-statements now directly support multiple context managers', DeprecationWarning ) # Filter the backports package UserWarning about being re-imported warnings.filterwarnings( 'ignore', '^Module backports was already imported from (.*), but (.*) is being added to sys.path$', UserWarning ) # Import salt libs # We import log ASAP because we NEED to make sure that any logger instance salt # instantiates is using salt.log.setup.SaltLoggingClass import salt.log.setup # the try block below bypasses an issue at build time so that modules don't # cause the build to fail from salt.utils import migrations import salt.utils.kinds as kinds try: from salt.utils.zeromq import ip_bracket import salt.utils.parsers from salt.utils.verify import check_user, verify_env, verify_socket except ImportError as exc: if exc.args[0] != 'No module named _msgpack': raise from salt.exceptions import SaltSystemExit, SaltClientError, get_error_message # Let's instantiate log using salt.log.setup.logging.getLogger() so pylint # leaves us alone and stops complaining about an un-used import log = salt.log.setup.logging.getLogger(__name__) class DaemonsMixin(object): # pylint: disable=no-init ''' Uses the same functions for all daemons ''' def verify_hash_type(self): ''' Verify and display a nag-messsage to the log if vulnerable hash-type is used. :return: ''' if self.config['hash_type'].lower() in ['md5', 'sha1']: log.warning( 'IMPORTANT: Do not use %s hashing algorithm! Please set ' '"hash_type" to sha256 in Salt %s config!', self.config['hash_type'], self.__class__.__name__ ) def action_log_info(self, action): ''' Say daemon starting. :param action :return: ''' log.info('%s the Salt %s', action, self.__class__.__name__) def start_log_info(self): ''' Say daemon starting. :return: ''' log.info('The Salt %s is starting up', self.__class__.__name__) def shutdown_log_info(self): ''' Say daemon shutting down. :return: ''' log.info('The Salt %s is shut down', self.__class__.__name__) def environment_failure(self, error): ''' Log environment failure for the daemon and exit with the error code. :param error: :return: ''' log.exception( 'Failed to create environment for %s: %s', self.__class__.__name__, get_error_message(error) ) self.shutdown(error) class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: disable=no-init ''' Creates a master server ''' def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument if hasattr(self.master, 'process_manager'): # IofloMaster has no process manager # escalate signal to the process manager processes self.master.process_manager.stop_restarting() self.master.process_manager.send_signal_to_processes(signum) # kill any remaining processes self.master.process_manager.kill_children() super(Master, self)._handle_signals(signum, sigframe) def prepare(self): ''' Run the preparation sequence required to start a salt master server. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() ''' super(Master, self).prepare() try: if self.config['verify_env']: v_dirs = [ self.config['pki_dir'], os.path.join(self.config['pki_dir'], 'minions'), os.path.join(self.config['pki_dir'], 'minions_pre'), os.path.join(self.config['pki_dir'], 'minions_denied'), os.path.join(self.config['pki_dir'], 'minions_autosign'), os.path.join(self.config['pki_dir'], 'minions_rejected'), self.config['cachedir'], os.path.join(self.config['cachedir'], 'jobs'), os.path.join(self.config['cachedir'], 'proc'), self.config['sock_dir'], self.config['token_dir'], self.config['syndic_dir'], self.config['sqlite_queue_dir'], ] if self.config.get('transport') == 'raet': v_dirs.append(os.path.join(self.config['pki_dir'], 'accepted')) v_dirs.append(os.path.join(self.config['pki_dir'], 'pending')) v_dirs.append(os.path.join(self.config['pki_dir'], 'rejected')) v_dirs.append(os.path.join(self.config['cachedir'], 'raet')) verify_env( v_dirs, self.config['user'], permissive=self.config['permissive_pki_access'], root_dir=self.config['root_dir'], pki_dir=self.config['pki_dir'], ) # Clear out syndics from cachedir for syndic_file in os.listdir(self.config['syndic_dir']): os.remove(os.path.join(self.config['syndic_dir'], syndic_file)) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) self.action_log_info('Setting up') # TODO: AIO core is separate from transport if self.config['transport'].lower() in ('zeromq', 'tcp'): if not verify_socket(self.config['interface'], self.config['publish_port'], self.config['ret_port']): self.shutdown(4, 'The ports are not available to bind') self.config['interface'] = ip_bracket(self.config['interface']) migrations.migrate_paths(self.config) # Late import so logging works correctly import salt.master self.master = salt.master.Master(self.config) else: # Add a udp port check here import salt.daemons.flo self.master = salt.daemons.flo.IofloMaster(self.config) self.daemonize_if_required() self.set_pidfile() salt.utils.process.notify_systemd() def start(self): ''' Start the actual master. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. ''' super(Master, self).start() if check_user(self.config['user']): self.action_log_info('Starting up') self.verify_hash_type() self.master.start() def shutdown(self, exitcode=0, exitmsg=None): ''' If sub-classed, run any shutdown operations on this method. ''' self.shutdown_log_info() msg = 'The salt master is shutdown. ' if exitmsg is not None: exitmsg = msg + exitmsg else: exitmsg = msg.strip() super(Master, self).shutdown(exitcode, exitmsg) class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: disable=no-init ''' Create a minion server ''' def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument # escalate signal to the process manager processes if hasattr(self.minion, 'stop'): self.minion.stop(signum) super(Minion, self)._handle_signals(signum, sigframe) # pylint: disable=no-member def prepare(self): ''' Run the preparation sequence required to start a salt minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() ''' super(Minion, self).prepare() try: if self.config['verify_env']: confd = self.config.get('default_include') if confd: # If 'default_include' is specified in config, then use it if '*' in confd: # Value is of the form "minion.d/*.conf" confd = os.path.dirname(confd) if not os.path.isabs(confd): # If configured 'default_include' is not an absolute # path, consider it relative to folder of 'conf_file' # (/etc/salt by default) confd = os.path.join( os.path.dirname(self.config['conf_file']), confd ) else: confd = os.path.join( os.path.dirname(self.config['conf_file']), 'minion.d' ) v_dirs = [ self.config['pki_dir'], self.config['cachedir'], self.config['sock_dir'], self.config['extension_modules'], confd, ] if self.config.get('transport') == 'raet': v_dirs.append(os.path.join(self.config['pki_dir'], 'accepted')) v_dirs.append(os.path.join(self.config['pki_dir'], 'pending')) v_dirs.append(os.path.join(self.config['pki_dir'], 'rejected')) v_dirs.append(os.path.join(self.config['cachedir'], 'raet')) verify_env( v_dirs, self.config['user'], permissive=self.config['permissive_pki_access'], root_dir=self.config['root_dir'], pki_dir=self.config['pki_dir'], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) log.info('Setting up the Salt Minion "%s"', self.config['id']) migrations.migrate_paths(self.config) # Bail out if we find a process running and it matches out pidfile if self.check_running(): self.action_log_info('An instance is already running. Exiting') self.shutdown(1) transport = self.config.get('transport').lower() # TODO: AIO core is separate from transport if transport in ('zeromq', 'tcp', 'detect'): # Late import so logging works correctly import salt.minion # If the minion key has not been accepted, then Salt enters a loop # waiting for it, if we daemonize later then the minion could halt # the boot process waiting for a key to be accepted on the master. # This is the latest safe place to daemonize self.daemonize_if_required() self.set_pidfile() if self.config.get('master_type') == 'func': salt.minion.eval_master_func(self.config) self.minion = salt.minion.MinionManager(self.config) elif transport == 'raet': import salt.daemons.flo self.daemonize_if_required() self.set_pidfile() self.minion = salt.daemons.flo.IofloMinion(self.config) else: log.error( 'The transport \'%s\' is not supported. Please use one of ' 'the following: tcp, raet, or zeromq.', transport ) self.shutdown(1) def start(self): ''' Start the actual minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. ''' super(Minion, self).start() while True: try: self._real_start() except SaltClientError as exc: # Restart for multi_master failover when daemonized if self.options.daemon: continue break def _real_start(self): try: if check_user(self.config['user']): self.action_log_info('Starting up') self.verify_hash_type() self.minion.tune_in() if self.minion.restart: raise SaltClientError('Minion could not connect to Master') except (KeyboardInterrupt, SaltSystemExit) as error: self.action_log_info('Stopping') if isinstance(error, KeyboardInterrupt): log.warning('Exiting on Ctrl-c') self.shutdown() else: log.error(error) self.shutdown(error.code) def call(self, cleanup_protecteds): ''' Start the actual minion as a caller minion. cleanup_protecteds is list of yard host addresses that should not be cleaned up this is to fix race condition when salt-caller minion starts up If sub-classed, don't **ever** forget to run: super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. ''' try: self.prepare() if check_user(self.config['user']): self.minion.opts['__role'] = kinds.APPL_KIND_NAMES[kinds.applKinds.caller] self.minion.opts['raet_cleanup_protecteds'] = cleanup_protecteds self.minion.call_in() except (KeyboardInterrupt, SaltSystemExit) as exc: self.action_log_info('Stopping') if isinstance(exc, KeyboardInterrupt): log.warning('Exiting on Ctrl-c') self.shutdown() else: log.error(exc) self.shutdown(exc.code) def shutdown(self, exitcode=0, exitmsg=None): ''' If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg ''' self.action_log_info('Shutting down') if hasattr(self, 'minion') and hasattr(self.minion, 'destroy'): self.minion.destroy() super(Minion, self).shutdown( exitcode, ('The Salt {0} is shutdown. {1}'.format( self.__class__.__name__, (exitmsg or '')).strip())) # pylint: enable=no-member class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # pylint: disable=no-init ''' Create a proxy minion server ''' def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument # escalate signal to the process manager processes self.minion.stop(signum) super(ProxyMinion, self)._handle_signals(signum, sigframe) # pylint: disable=no-member def prepare(self): ''' Run the preparation sequence required to start a salt proxy minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() ''' super(ProxyMinion, self).prepare() if not self.values.proxyid: self.error('salt-proxy requires --proxyid') # Proxies get their ID from the command line. This may need to change in # the future. # We used to set this here. Now it is set in ProxyMinionOptionParser # by passing it via setup_config to config.minion_config # self.config['id'] = self.values.proxyid try: if self.config['verify_env']: confd = self.config.get('default_include') if confd: # If 'default_include' is specified in config, then use it if '*' in confd: # Value is of the form "minion.d/*.conf" confd = os.path.dirname(confd) if not os.path.isabs(confd): # If configured 'default_include' is not an absolute # path, consider it relative to folder of 'conf_file' # (/etc/salt by default) confd = os.path.join( os.path.dirname(self.config['conf_file']), confd ) else: confd = os.path.join( os.path.dirname(self.config['conf_file']), 'proxy.d' ) v_dirs = [ self.config['pki_dir'], self.config['cachedir'], self.config['sock_dir'], self.config['extension_modules'], confd, ] if self.config.get('transport') == 'raet': v_dirs.append(os.path.join(self.config['pki_dir'], 'accepted')) v_dirs.append(os.path.join(self.config['pki_dir'], 'pending')) v_dirs.append(os.path.join(self.config['pki_dir'], 'rejected')) v_dirs.append(os.path.join(self.config['cachedir'], 'raet')) verify_env( v_dirs, self.config['user'], permissive=self.config['permissive_pki_access'], root_dir=self.config['root_dir'], pki_dir=self.config['pki_dir'], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) self.action_log_info('Setting up "{0}"'.format(self.config['id'])) migrations.migrate_paths(self.config) # Bail out if we find a process running and it matches out pidfile if self.check_running(): self.action_log_info('An instance is already running. Exiting') self.shutdown(1) # TODO: AIO core is separate from transport if self.config['transport'].lower() in ('zeromq', 'tcp', 'detect'): # Late import so logging works correctly import salt.minion # If the minion key has not been accepted, then Salt enters a loop # waiting for it, if we daemonize later then the minion could halt # the boot process waiting for a key to be accepted on the master. # This is the latest safe place to daemonize self.daemonize_if_required() self.set_pidfile() if self.config.get('master_type') == 'func': salt.minion.eval_master_func(self.config) self.minion = salt.minion.ProxyMinionManager(self.config) else: # For proxy minions, this doesn't work yet. import salt.daemons.flo self.daemonize_if_required() self.set_pidfile() self.minion = salt.daemons.flo.IofloMinion(self.config) def start(self): ''' Start the actual proxy minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. ''' super(ProxyMinion, self).start() try: if check_user(self.config['user']): self.action_log_info('The Proxy Minion is starting up') self.verify_hash_type() self.minion.tune_in() if self.minion.restart: raise SaltClientError('Proxy Minion could not connect to Master') except (KeyboardInterrupt, SaltSystemExit) as exc: self.action_log_info('Proxy Minion Stopping') if isinstance(exc, KeyboardInterrupt): log.warning('Exiting on Ctrl-c') self.shutdown() else: log.error(exc) self.shutdown(exc.code) # def call(self, cleanup_protecteds): # This fn is omitted here, proxy minions have never supported RAET def shutdown(self, exitcode=0, exitmsg=None): ''' If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg ''' if hasattr(self, 'minion') and 'proxymodule' in self.minion.opts: proxy_fn = self.minion.opts['proxymodule'].loaded_base_name + '.shutdown' self.minion.opts['proxymodule'][proxy_fn](self.minion.opts) self.action_log_info('Shutting down') super(ProxyMinion, self).shutdown( exitcode, ('The Salt {0} is shutdown. {1}'.format( self.__class__.__name__, (exitmsg or '')).strip())) # pylint: enable=no-member class Syndic(salt.utils.parsers.SyndicOptionParser, DaemonsMixin): # pylint: disable=no-init ''' Create a syndic server ''' def prepare(self): ''' Run the preparation sequence required to start a salt syndic minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() ''' super(Syndic, self).prepare() try: if self.config['verify_env']: verify_env( [ self.config['pki_dir'], self.config['cachedir'], self.config['sock_dir'], self.config['extension_modules'], ], self.config['user'], permissive=self.config['permissive_pki_access'], root_dir=self.config['root_dir'], pki_dir=self.config['pki_dir'], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) self.action_log_info('Setting up "{0}"'.format(self.config['id'])) # Late import so logging works correctly import salt.minion self.daemonize_if_required() self.syndic = salt.minion.SyndicManager(self.config) self.set_pidfile() def start(self): ''' Start the actual syndic. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. ''' super(Syndic, self).start() if check_user(self.config['user']): self.action_log_info('Starting up') self.verify_hash_type() try: self.syndic.tune_in() except KeyboardInterrupt: self.action_log_info('Stopping') self.shutdown() def shutdown(self, exitcode=0, exitmsg=None): ''' If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg ''' self.action_log_info('Shutting down') super(Syndic, self).shutdown( exitcode, ('The Salt {0} is shutdown. {1}'.format( self.__class__.__name__, (exitmsg or '')).strip()))