mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Parsers(master, minion, syndic) cleanup code.
Moved the minion, master and syndic parsers to use mixins to clean up, reuse, and automate some of the required procedures.
This commit is contained in:
parent
5f3552862b
commit
d776705e49
8 changed files with 543 additions and 334 deletions
350
salt/__init__.py
350
salt/__init__.py
|
@ -1,7 +1,6 @@
|
|||
'''
|
||||
Make me some salt!
|
||||
'''
|
||||
from salt.version import __version__
|
||||
|
||||
# Import python libs
|
||||
import os
|
||||
|
@ -9,370 +8,143 @@ import sys
|
|||
|
||||
# Import salt libs, the try block bypasses an issue at build time so that c
|
||||
# modules don't cause the build to fail
|
||||
from salt.version import __version__
|
||||
|
||||
try:
|
||||
import salt.config
|
||||
from salt.utils import parser as optparse
|
||||
from salt.utils.process import set_pidfile
|
||||
from salt.utils import parsers
|
||||
from salt.utils.verify import check_user, verify_env, verify_socket
|
||||
except ImportError as e:
|
||||
if e.args[0] != 'No module named _msgpack':
|
||||
raise
|
||||
|
||||
class Master(object):
|
||||
class Master(parsers.MasterOptionParser):
|
||||
'''
|
||||
Creates a master server
|
||||
'''
|
||||
def __init__(self):
|
||||
self.cli = self.__parse_cli()
|
||||
# command line overrides config
|
||||
if self.cli['user']:
|
||||
self.opts['user'] = self.cli['user']
|
||||
|
||||
# Send the pidfile location to the opts
|
||||
if self.cli['pidfile']:
|
||||
self.opts['pidfile'] = self.cli['pidfile']
|
||||
|
||||
def __parse_cli(self):
|
||||
'''
|
||||
Parse the cli for options passed to a master daemon
|
||||
'''
|
||||
import salt.log
|
||||
parser = optparse.OptionParser(version="%%prog %s" % __version__)
|
||||
parser.add_option('-d',
|
||||
'--daemon',
|
||||
dest='daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the master as a daemon')
|
||||
parser.add_option('-c',
|
||||
'--config',
|
||||
dest='config',
|
||||
default='/etc/salt/master',
|
||||
help='Pass in an alternative configuration file')
|
||||
parser.add_option('-u',
|
||||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run master')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
help=('Specify the location of the pidfile.'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
choices=list(salt.log.LOG_LEVELS),
|
||||
help='Console log level. One of %s. For the logfile settings '
|
||||
'see the config file. Default: \'warning\'.' %
|
||||
', '.join([repr(l) for l in salt.log.SORTED_LEVEL_NAMES]))
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
self.opts = salt.config.master_config(options.config)
|
||||
|
||||
if not options.log_level:
|
||||
options.log_level = self.opts['log_level']
|
||||
|
||||
salt.log.setup_console_logger(
|
||||
options.log_level,
|
||||
log_format=self.opts['log_fmt_console'],
|
||||
date_format=self.opts['log_datefmt']
|
||||
)
|
||||
|
||||
cli = {'daemon': options.daemon,
|
||||
'config': options.config,
|
||||
'user': options.user,
|
||||
'pidfile': options.pidfile}
|
||||
|
||||
return cli
|
||||
|
||||
def start(self):
|
||||
'''
|
||||
Run the sequence to start a salt master server
|
||||
'''
|
||||
self.parse_args()
|
||||
|
||||
try:
|
||||
verify_env([
|
||||
self.opts['pki_dir'],
|
||||
os.path.join(self.opts['pki_dir'], 'minions'),
|
||||
os.path.join(self.opts['pki_dir'], 'minions_pre'),
|
||||
os.path.join(self.opts['pki_dir'], 'minions_rejected'),
|
||||
self.opts['cachedir'],
|
||||
os.path.join(self.opts['cachedir'], 'jobs'),
|
||||
os.path.dirname(self.opts['log_file']),
|
||||
self.opts['sock_dir'],
|
||||
], self.opts['user'],
|
||||
permissive=self.opts['permissive_pki_access'])
|
||||
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_rejected'),
|
||||
self.config['cachedir'],
|
||||
os.path.join(self.config['cachedir'], 'jobs'),
|
||||
os.path.dirname(self.config['log_file']),
|
||||
self.config['sock_dir'],
|
||||
], self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'])
|
||||
except OSError, err:
|
||||
sys.exit(err.errno)
|
||||
|
||||
import salt.log
|
||||
salt.log.setup_logfile_logger(
|
||||
self.opts['log_file'],
|
||||
self.opts['log_level_logfile'] or self.opts['log_level'],
|
||||
log_format=self.opts['log_fmt_logfile'],
|
||||
date_format=self.opts['log_datefmt']
|
||||
)
|
||||
for name, level in self.opts['log_granular_levels'].items():
|
||||
salt.log.set_logger_level(name, level)
|
||||
self.setup_logfile_logger()
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
# Late import so logging works correctly
|
||||
if not verify_socket(
|
||||
self.opts['interface'],
|
||||
self.opts['publish_port'],
|
||||
self.opts['ret_port']
|
||||
):
|
||||
log.critical('The ports are not available to bind')
|
||||
sys.exit(4)
|
||||
if not verify_socket(self.config['interface'],
|
||||
self.config['publish_port'],
|
||||
self.config['ret_port']):
|
||||
self.exit(4, 'The ports are not available to bind')
|
||||
|
||||
import salt.master
|
||||
master = salt.master.Master(self.opts)
|
||||
if self.cli['daemon']:
|
||||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
set_pidfile(self.opts['pidfile'])
|
||||
if check_user(self.opts['user'], log):
|
||||
master = salt.master.Master(self.config)
|
||||
self.daemonize_if_required()
|
||||
self.set_pidfile()
|
||||
if check_user(self.config['user'], log):
|
||||
try:
|
||||
master.start()
|
||||
except salt.master.MasterExit:
|
||||
sys.exit()
|
||||
|
||||
|
||||
class Minion(object):
|
||||
class Minion(parsers.MinionOptionParser):
|
||||
'''
|
||||
Create a minion server
|
||||
'''
|
||||
def __init__(self):
|
||||
self.cli = self.__parse_cli()
|
||||
# command line overrides config
|
||||
if self.cli['user']:
|
||||
self.opts['user'] = self.cli['user']
|
||||
|
||||
def __parse_cli(self):
|
||||
'''
|
||||
Parse the cli input
|
||||
'''
|
||||
import salt.log
|
||||
parser = optparse.OptionParser(version="%%prog %s" % __version__)
|
||||
parser.add_option('-d',
|
||||
'--daemon',
|
||||
dest='daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the minion as a daemon')
|
||||
parser.add_option('-c',
|
||||
'--config',
|
||||
dest='config',
|
||||
default='/etc/salt/minion',
|
||||
help='Pass in an alternative configuration file')
|
||||
parser.add_option('-u',
|
||||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run minion')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
default='/var/run/salt-minion.pid',
|
||||
help=('Specify the location of the pidfile. Default'
|
||||
' %default'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
choices=list(salt.log.LOG_LEVELS),
|
||||
help='Console log level. One of %s. For the logfile settings '
|
||||
'see the config file. Default: \'warning\'.' %
|
||||
', '.join([repr(l) for l in salt.log.SORTED_LEVEL_NAMES]))
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
self.opts = salt.config.minion_config(options.config)
|
||||
|
||||
if not options.log_level:
|
||||
options.log_level = self.opts['log_level']
|
||||
|
||||
salt.log.setup_console_logger(
|
||||
options.log_level,
|
||||
log_format=self.opts['log_fmt_console'],
|
||||
date_format=self.opts['log_datefmt']
|
||||
)
|
||||
|
||||
cli = {'daemon': options.daemon,
|
||||
'config': options.config,
|
||||
'user': options.user,
|
||||
'pidfile': options.pidfile}
|
||||
|
||||
return cli
|
||||
|
||||
def start(self):
|
||||
'''
|
||||
Execute this method to start up a minion.
|
||||
'''
|
||||
self.parse_args()
|
||||
|
||||
try:
|
||||
verify_env([
|
||||
self.opts['pki_dir'],
|
||||
self.opts['cachedir'],
|
||||
self.opts['sock_dir'],
|
||||
self.opts['extension_modules'],
|
||||
os.path.dirname(self.opts['log_file']),
|
||||
], self.opts['user'],
|
||||
permissive=self.opts['permissive_pki_access'])
|
||||
self.config['pki_dir'],
|
||||
self.config['cachedir'],
|
||||
self.config['sock_dir'],
|
||||
self.config['extension_modules'],
|
||||
os.path.dirname(self.config['log_file']),
|
||||
], self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'])
|
||||
except OSError, err:
|
||||
sys.exit(err.errno)
|
||||
|
||||
import salt.log
|
||||
salt.log.setup_logfile_logger(
|
||||
self.opts['log_file'],
|
||||
self.opts['log_level_logfile'] or self.opts['log_level'],
|
||||
log_format=self.opts['log_fmt_logfile'],
|
||||
date_format=self.opts['log_datefmt']
|
||||
)
|
||||
for name, level in self.opts['log_granular_levels'].items():
|
||||
salt.log.set_logger_level(name, level)
|
||||
self.setup_logfile_logger()
|
||||
|
||||
import logging
|
||||
# Late import so logging works correctly
|
||||
import salt.minion
|
||||
log = logging.getLogger(__name__)
|
||||
if self.cli['daemon']:
|
||||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
# 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
|
||||
salt.utils.daemonize()
|
||||
# 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()
|
||||
try:
|
||||
minion = salt.minion.Minion(self.opts)
|
||||
set_pidfile(self.cli['pidfile'])
|
||||
if check_user(self.opts['user'], log):
|
||||
minion = salt.minion.Minion(self.config)
|
||||
self.set_pidfile()
|
||||
if check_user(self.config['user'], log):
|
||||
minion.tune_in()
|
||||
except KeyboardInterrupt:
|
||||
log.warn('Stopping the Salt Minion')
|
||||
raise SystemExit('\nExiting on Ctrl-c')
|
||||
|
||||
|
||||
class Syndic(object):
|
||||
class Syndic(parsers.SyndicOptionParser):
|
||||
'''
|
||||
Create a syndic server
|
||||
'''
|
||||
def __init__(self):
|
||||
self.cli = self.__parse_cli()
|
||||
# command line overrides config
|
||||
if self.cli['user']:
|
||||
self.opts['user'] = self.cli['user']
|
||||
|
||||
def __prep_opts(self, cli):
|
||||
'''
|
||||
Generate the opts used by the syndic
|
||||
'''
|
||||
opts = salt.config.master_config(cli['master_config'])
|
||||
opts['_minion_conf_file'] = opts['conf_file']
|
||||
opts.update(salt.config.minion_config(cli['minion_config']))
|
||||
if 'syndic_master' in opts:
|
||||
# Some of the opts need to be changed to match the needed opts
|
||||
# in the minion class.
|
||||
opts['master'] = opts['syndic_master']
|
||||
opts['master_ip'] = salt.utils.dns_check(opts['master'])
|
||||
|
||||
opts['master_uri'] = ('tcp://' + opts['master_ip'] +
|
||||
':' + str(opts['master_port']))
|
||||
opts['_master_conf_file'] = opts['conf_file']
|
||||
opts.pop('conf_file')
|
||||
return opts
|
||||
err = ('The syndic_master needs to be configured in the salt master '
|
||||
'config, EXITING!\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(2)
|
||||
|
||||
def __parse_cli(self):
|
||||
'''
|
||||
Parse the cli for options passed to a syndic daemon
|
||||
'''
|
||||
import salt.log
|
||||
parser = optparse.OptionParser(version="%%prog %s" % __version__)
|
||||
parser.add_option('-d',
|
||||
'--daemon',
|
||||
dest='daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the syndic as a daemon')
|
||||
parser.add_option('--master-config',
|
||||
dest='master_config',
|
||||
default='/etc/salt/master',
|
||||
help='Pass in an alternative master configuration file')
|
||||
parser.add_option('--minion-config',
|
||||
dest='minion_config',
|
||||
default='/etc/salt/minion',
|
||||
help='Pass in an alternative minion configuration file')
|
||||
parser.add_option('-u',
|
||||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run syndic')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
default='/var/run/salt-syndic.pid',
|
||||
help=('Specify the location of the pidfile. Default'
|
||||
' %default'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
choices=list(salt.log.LOG_LEVELS),
|
||||
help='Console log level. One of %s. For the logfile settings '
|
||||
'see the config file. Default: \'warning\'.' %
|
||||
', '.join([repr(l) for l in salt.log.LOG_LEVELS]))
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
cli = {'daemon': options.daemon,
|
||||
'minion_config': options.minion_config,
|
||||
'master_config': options.master_config,
|
||||
'pidfile': options.pidfile,
|
||||
'user': options.user}
|
||||
|
||||
self.opts = self.__prep_opts(cli)
|
||||
|
||||
if not options.log_level:
|
||||
options.log_level = self.opts['log_level']
|
||||
|
||||
salt.log.setup_console_logger(
|
||||
options.log_level,
|
||||
log_format=self.opts['log_fmt_console'],
|
||||
date_format=self.opts['log_datefmt']
|
||||
)
|
||||
|
||||
return cli
|
||||
|
||||
def start(self):
|
||||
'''
|
||||
Execute this method to start up a syndic.
|
||||
'''
|
||||
self.parse_args()
|
||||
try:
|
||||
verify_env([
|
||||
self.opts['pki_dir'], self.opts['cachedir'],
|
||||
os.path.dirname(self.opts['log_file']),
|
||||
], self.opts['user'],
|
||||
permissive=self.opts['permissive_pki_access'])
|
||||
self.config['pki_dir'], self.config['cachedir'],
|
||||
os.path.dirname(self.config['log_file']),
|
||||
],
|
||||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access']
|
||||
)
|
||||
except OSError, err:
|
||||
sys.exit(err.errno)
|
||||
import salt.log
|
||||
salt.log.setup_logfile_logger(
|
||||
self.opts['log_file'], self.opts['log_level']
|
||||
)
|
||||
for name, level in self.opts['log_granular_levels'].items():
|
||||
salt.log.set_logger_level(name, level)
|
||||
|
||||
|
||||
self.setup_logfile_logger()
|
||||
|
||||
import logging
|
||||
|
||||
# Late import so logging works correctly
|
||||
import salt.minion
|
||||
log = logging.getLogger(__name__)
|
||||
if self.cli['daemon']:
|
||||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
set_pidfile(self.cli['pidfile'])
|
||||
if check_user(self.opts['user'], log):
|
||||
|
||||
self.daemonize_if_required()
|
||||
self.set_pidfile()
|
||||
|
||||
if check_user(self.config['user'], log):
|
||||
try:
|
||||
syndic = salt.minion.Syndic(self.opts)
|
||||
syndic = salt.minion.Syndic(self.config)
|
||||
syndic.tune_in()
|
||||
except KeyboardInterrupt:
|
||||
log.warn('Stopping the Salt Syndic Minion')
|
||||
|
|
|
@ -15,9 +15,10 @@ import salt.client
|
|||
import salt.output
|
||||
import salt.runner
|
||||
|
||||
from salt.utils import parser as optparse
|
||||
#from salt.utils import parsers as optparse
|
||||
import optparse
|
||||
from salt.utils.verify import verify_env
|
||||
from salt import __version__ as VERSION
|
||||
from salt.version import __version__ as VERSION
|
||||
from salt.exceptions import SaltInvocationError, SaltClientError, \
|
||||
SaltException
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
salt.utils.parser
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
:copyright: © 2012 UfSoft.org - :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
from salt import version
|
||||
|
||||
|
||||
class OptionParser(optparse.OptionParser):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("version", version.__version__)
|
||||
kwargs.setdefault('usage', '%prog')
|
||||
optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
|
||||
def parse_args(self, args=None, values=None):
|
||||
options, args = optparse.OptionParser.parse_args(self, args, values)
|
||||
if options.versions_report:
|
||||
self.print_versions_report()
|
||||
return options, args
|
||||
|
||||
def _add_version_option(self):
|
||||
optparse.OptionParser._add_version_option(self)
|
||||
self.add_option(
|
||||
'--versions-report', action='store_true',
|
||||
help="show program's dependencies version number and exit"
|
||||
)
|
||||
|
||||
def print_versions_report(self, file=sys.stdout):
|
||||
print >> file, '\n'.join(version.versions_report())
|
||||
self.exit()
|
353
salt/utils/parsers.py
Normal file
353
salt/utils/parsers.py
Normal file
|
@ -0,0 +1,353 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
salt.utils.parser
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
:copyright: © 2012 UfSoft.org - :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import optparse
|
||||
from salt import config, log, version
|
||||
|
||||
def _sorted(mixins_or_funcs):
|
||||
return sorted(
|
||||
mixins_or_funcs, key=lambda mf: getattr(mf, '_mixin_prio_', 1000)
|
||||
)
|
||||
|
||||
|
||||
class MixInMeta(type):
|
||||
|
||||
# This attribute here won't actually do anything. But, if you need to
|
||||
# specify an order or a dependency within the mix-ins, please define the
|
||||
# attribute on your own MixIn
|
||||
_mixin_prio_ = 0
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
instance = super(MixInMeta, cls).__new__(cls, name, bases, attrs)
|
||||
if not hasattr(instance, '_mixin_setup'):
|
||||
raise RuntimeError(
|
||||
"Don't subclass {0} in {1} if you're not going to use it as a "
|
||||
"salt parser mix-in.".format(cls.__name__, name)
|
||||
)
|
||||
return instance
|
||||
|
||||
|
||||
class OptionParserMeta(MixInMeta):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
instance = super(OptionParserMeta, cls).__new__(cls, name, bases, attrs)
|
||||
if not hasattr(instance, '_mixin_setup_funcs'):
|
||||
instance._mixin_setup_funcs = []
|
||||
if not hasattr(instance, '_mixin_process_funcs'):
|
||||
instance._mixin_process_funcs = []
|
||||
if not hasattr(instance, '_mixin_after_parsed_funcs'):
|
||||
instance._mixin_after_parsed_funcs = []
|
||||
|
||||
for base in _sorted(bases+(instance,)):
|
||||
func = getattr(base, '_mixin_setup', None)
|
||||
if func is not None and func not in instance._mixin_setup_funcs:
|
||||
instance._mixin_setup_funcs.append(func)
|
||||
|
||||
func = getattr(base, '_mixin_after_parsed', None)
|
||||
if func is not None and func not in instance._mixin_after_parsed_funcs:
|
||||
instance._mixin_after_parsed_funcs.append(func)
|
||||
|
||||
# Mark process_<opt> functions with the base priority for sorting
|
||||
for func in dir(base):
|
||||
if not func.startswith('process_'):
|
||||
continue
|
||||
func = getattr(base, func)
|
||||
if getattr(func, '_mixin_prio_', None) is not None:
|
||||
# Function already has the attribute set, don't override it
|
||||
continue
|
||||
func.__func__._mixin_prio_ = getattr(base, '_mixin_prio_', 1000)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class OptionParser(optparse.OptionParser):
|
||||
usage = "%prog"
|
||||
|
||||
epilog = ("You can find additional help about %prog issuing 'man %prog' "
|
||||
"or on http://docs.saltstack.org/en/latest/index.html")
|
||||
description = None
|
||||
|
||||
# Private attributes
|
||||
_mixin_prio_ = 100
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("version", "%prog {0}".format(version.__version__))
|
||||
kwargs.setdefault("usage", self.usage)
|
||||
if self.description:
|
||||
kwargs.setdefault('description', self.description)
|
||||
|
||||
if self.epilog:
|
||||
kwargs.setdefault('epilog', self.epilog)
|
||||
|
||||
optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
|
||||
if "%prog" in self.epilog:
|
||||
self.epilog = self.epilog.replace("%prog", self.get_prog_name())
|
||||
|
||||
def parse_args(self, args=None, values=None):
|
||||
options, args = optparse.OptionParser.parse_args(self, args, values)
|
||||
if options.versions_report:
|
||||
self.print_versions_report()
|
||||
|
||||
self.options, self.args = options, args
|
||||
|
||||
# Gather and run the process_<option> functions in the proper order
|
||||
process_option_funcs = []
|
||||
for option_key in options.__dict__.keys():
|
||||
process_option_func = getattr(self, "process_%s" % option_key, None)
|
||||
if process_option_func is not None:
|
||||
process_option_funcs.append(process_option_func)
|
||||
|
||||
for process_option_func in _sorted(process_option_funcs):
|
||||
process_option_func()
|
||||
|
||||
# Run the functions on self._mixin_after_parsed_funcs
|
||||
for mixin_after_parsed_func in self._mixin_after_parsed_funcs:
|
||||
mixin_after_parsed_func(self)
|
||||
|
||||
# Retain the standard behaviour of optparse to return options and args
|
||||
return options, args
|
||||
|
||||
def _populate_option_list(self, option_list, add_help=True):
|
||||
optparse.OptionParser._populate_option_list(
|
||||
self, option_list, add_help=add_help
|
||||
)
|
||||
for mixin_setup_func in self._mixin_setup_funcs:
|
||||
mixin_setup_func(self)
|
||||
|
||||
def _add_version_option(self):
|
||||
optparse.OptionParser._add_version_option(self)
|
||||
self.add_option(
|
||||
'--versions-report', action='store_true',
|
||||
help="show program's dependencies version number and exit"
|
||||
)
|
||||
|
||||
def print_versions_report(self, file=sys.stdout):
|
||||
print >> file, '\n'.join(version.versions_report())
|
||||
self.exit()
|
||||
|
||||
|
||||
class DeprecatedConfigMessage(object):
|
||||
_mixin_prio_ = -10
|
||||
|
||||
def print_config_warning(self, *args, **kwargs):
|
||||
self.error(
|
||||
"The '-c/--config' option is deprecated. You should now use "
|
||||
"-c/--config-dir to point to a directory which holds all of "
|
||||
"salt's configuration files.\n"
|
||||
)
|
||||
|
||||
class ConfigDirMixIn(DeprecatedConfigMessage):
|
||||
__metaclass__ = MixInMeta
|
||||
|
||||
def _mixin_setup(self):
|
||||
self.add_option(
|
||||
'-c', '--config-dir', default='/etc/salt',
|
||||
help=('Pass in an alternative configuration directory. Default: '
|
||||
'%default')
|
||||
)
|
||||
|
||||
def __merge_config_with_cli(self, *args):
|
||||
for option in self.option_list:
|
||||
if not option.dest:
|
||||
# --version does not have dest attribute set for example.
|
||||
# All options defined by us, even if not explicitly(by kwarg),
|
||||
# will have the dest attribute set
|
||||
continue
|
||||
|
||||
value = getattr(self.options, option.dest, None)
|
||||
if value:
|
||||
self.config[option.dest] = value
|
||||
|
||||
def process_config_dir(self):
|
||||
# XXX: Remove deprecation warning in next release
|
||||
if os.path.isfile(self.options.config_dir):
|
||||
self.print_config_warning()
|
||||
|
||||
if hasattr(self, 'setup_config'):
|
||||
self.config = self.setup_config()
|
||||
# Add an additional function that will merge the cli options with
|
||||
# the config options and if needed override them
|
||||
self._mixin_after_parsed_funcs.append(self.__merge_config_with_cli)
|
||||
|
||||
def get_config_file_path(self, configfile):
|
||||
return os.path.join(self.options.config_dir, configfile)
|
||||
|
||||
|
||||
class DeprecatedMasterMinionMixIn(DeprecatedConfigMessage):
|
||||
__metaclass__ = MixInMeta
|
||||
|
||||
def _mixin_setup(self):
|
||||
# XXX: Remove deprecated option in next release
|
||||
self.add_option(
|
||||
'--config', action="callback", callback=self.print_config_warning,
|
||||
help='DEPRECATED. Please use -c/--config-dir from now on.'
|
||||
)
|
||||
|
||||
|
||||
class DeprecatedSyndicOptionsMixIn(DeprecatedConfigMessage):
|
||||
__metaclass__ = MixInMeta
|
||||
|
||||
def _mixin_setup(self):
|
||||
# XXX: Remove deprecated option in next release
|
||||
self.add_option(
|
||||
'--master-config', '--minion-config',
|
||||
action="callback", callback=self.print_config_warning,
|
||||
help='DEPRECATED. Please use -c/--config-dir from now on.'
|
||||
)
|
||||
|
||||
def process_config_dir(self, options):
|
||||
# XXX: Remove deprecation warning in next release
|
||||
if os.path.isfile(options.config_dir):
|
||||
self.print_config_warning()
|
||||
|
||||
if hasattr(self, 'setup_config'):
|
||||
self.config = self.setup_config()
|
||||
|
||||
|
||||
class LogLevelMixIn(object):
|
||||
__metaclass__ = MixInMeta
|
||||
_mixin_prio_ = 10
|
||||
_skip_console_logging_config_ = False
|
||||
|
||||
def _mixin_setup(self):
|
||||
if getattr(self, '_skip_console_logging_config_', False):
|
||||
return
|
||||
|
||||
self.add_option(
|
||||
'-l', '--log-level',
|
||||
choices=list(log.LOG_LEVELS),
|
||||
help=('Logging log level. One of {0}. For the logfile settings see '
|
||||
'the configuration file. Default: \'warning\'.').format(
|
||||
', '.join([repr(l) for l in log.SORTED_LEVEL_NAMES])
|
||||
)
|
||||
)
|
||||
|
||||
def process_log_level(self):
|
||||
if not self.options.log_level:
|
||||
self.options.log_level = self.config['log_level']
|
||||
|
||||
log.setup_console_logger(
|
||||
self.options.log_level,
|
||||
log_format=self.config['log_fmt_console'],
|
||||
date_format=self.config['log_datefmt']
|
||||
)
|
||||
|
||||
def setup_logfile_logger(self):
|
||||
log.setup_logfile_logger(
|
||||
self.config['log_file'],
|
||||
self.config['log_level_logfile'] or self.config['log_level'],
|
||||
log_format=self.config['log_fmt_logfile'],
|
||||
date_format=self.config['log_datefmt']
|
||||
)
|
||||
for name, level in self.config['log_granular_levels'].items():
|
||||
log.set_logger_level(name, level)
|
||||
|
||||
|
||||
class RunUserMixin(object):
|
||||
__metaclass__ = MixInMeta
|
||||
_mixin_prio_ = 20
|
||||
|
||||
def _mixin_setup(self):
|
||||
self.add_option(
|
||||
'-u', '--user',
|
||||
help='Specify user to run {0}'.format(self.get_prog_name())
|
||||
)
|
||||
|
||||
|
||||
class DaemonMixIn(object):
|
||||
__metaclass__ = MixInMeta
|
||||
_mixin_prio_ = 30
|
||||
|
||||
def _mixin_setup(self):
|
||||
self.add_option(
|
||||
'-d', '--daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the {0} as a daemon'.format(self.get_prog_name())
|
||||
)
|
||||
|
||||
def daemonize_if_required(self):
|
||||
if self.options.daemon:
|
||||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
|
||||
|
||||
class PidfileMixin(object):
|
||||
__metaclass__ = MixInMeta
|
||||
_mixin_prio_ = 40
|
||||
|
||||
def _mixin_setup(self):
|
||||
self.add_option(
|
||||
'--pid-file', dest='pidfile',
|
||||
default='/var/run/{0}.pid'.format(self.get_prog_name()),
|
||||
help=('Specify the location of the pidfile. Default: %default')
|
||||
)
|
||||
|
||||
def set_pidfile(self):
|
||||
from salt.utils.process import set_pidfile
|
||||
set_pidfile(self.config['pidfile'])
|
||||
|
||||
|
||||
class MasterOptionParser(OptionParser, ConfigDirMixIn, LogLevelMixIn,
|
||||
DeprecatedMasterMinionMixIn, RunUserMixin,
|
||||
DaemonMixIn, PidfileMixin):
|
||||
|
||||
__metaclass__ = OptionParserMeta
|
||||
|
||||
description = "TODO: explain what salt-master is"
|
||||
|
||||
def setup_config(self):
|
||||
return config.master_config(self.get_config_file_path('master'))
|
||||
|
||||
|
||||
class MinionOptionParser(MasterOptionParser):
|
||||
|
||||
__metaclass__ = OptionParserMeta
|
||||
|
||||
description = "TODO: explain what salt-minion is"
|
||||
|
||||
def setup_config(self, options):
|
||||
return config.minion_config(self.get_config_file_path('minion'))
|
||||
|
||||
|
||||
class SyndicOptionParser(OptionParser, DeprecatedSyndicOptionsMixIn,
|
||||
ConfigDirMixIn, LogLevelMixIn, RunUserMixin,
|
||||
DaemonMixIn, PidfileMixin):
|
||||
|
||||
__metaclass__ = OptionParserMeta
|
||||
|
||||
description = ("A seamless master of masters. Scale Salt to thousands of "
|
||||
"hosts or across many different networks.")
|
||||
|
||||
def setup_config(self, options):
|
||||
opts = config.master_config(self.get_config_file_path('master'))
|
||||
opts['_minion_conf_file'] = opts['conf_file']
|
||||
opts.update(config.minion_config(self.get_config_file_path('minion')))
|
||||
|
||||
if 'syndic_master' not in opts:
|
||||
self.error(
|
||||
"The syndic_master needs to be configured in the salt master "
|
||||
"config, EXITING!"
|
||||
)
|
||||
|
||||
from salt import utils
|
||||
# Some of the opts need to be changed to match the needed opts
|
||||
# in the minion class.
|
||||
opts['master'] = opts['syndic_master']
|
||||
opts['master_ip'] = utils.dns_check(opts['master'])
|
||||
|
||||
opts['master_uri'] = 'tcp://{0}:{1}'.format(
|
||||
opts['master_ip'], str(opts['master_port'])
|
||||
)
|
||||
opts['_master_conf_file'] = opts['conf_file']
|
||||
opts.pop('conf_file')
|
||||
return opts
|
|
@ -320,7 +320,7 @@ class ShellCase(TestCase):
|
|||
'''
|
||||
Execute a test for a shell command
|
||||
'''
|
||||
def run_script(self, script, arg_str):
|
||||
def run_script(self, script, arg_str, catch_stderr=False):
|
||||
'''
|
||||
Execute a script with the given argument string
|
||||
'''
|
||||
|
@ -329,11 +329,17 @@ class ShellCase(TestCase):
|
|||
return False
|
||||
ppath = 'PYTHONPATH={0}:{1}'.format(CODE_DIR, ':'.join(sys.path[1:]))
|
||||
cmd = '{0} {1} {2} {3}'.format(ppath, PYEXEC, path, arg_str)
|
||||
data = subprocess.Popen(cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE
|
||||
).communicate()[0].split('\n')
|
||||
return data
|
||||
|
||||
if catch_stderr:
|
||||
out, err = subprocess.Popen(
|
||||
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
).communicate()
|
||||
return out.split('\n'), err.split('\n')
|
||||
|
||||
data = subprocess.Popen(
|
||||
cmd, shell=True, stdout=subprocess.PIPE
|
||||
).communicate()
|
||||
return data[0].split('\n')
|
||||
|
||||
def run_salt(self, arg_str):
|
||||
'''
|
||||
|
@ -376,3 +382,33 @@ class ShellCase(TestCase):
|
|||
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
|
||||
arg_str = '-c {0} {1}'.format(mconf, arg_str)
|
||||
return self.run_script('salt-key', arg_str)
|
||||
|
||||
|
||||
class ShellCaseCommonTestsMixIn(object):
|
||||
|
||||
def test_deprecated_config(self):
|
||||
"""
|
||||
test for the --config deprecation warning
|
||||
|
||||
Once --config is fully deprecated, this test can be removed
|
||||
|
||||
"""
|
||||
|
||||
if getattr(self, '_call_binary_', None) is None:
|
||||
self.skipTest("'_call_binary_' not defined.")
|
||||
|
||||
cfgfile = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
|
||||
out, err = self.run_script(
|
||||
self._call_binary_, "--config {0}".format(cfgfile), catch_stderr=True
|
||||
)
|
||||
self.assertIn('Usage: {0}'.format(self._call_binary_), '\n'.join(err))
|
||||
self.assertIn('deprecated', '\n'.join(err))
|
||||
|
||||
|
||||
def test_version_includes_binary_name(self):
|
||||
if getattr(self, '_call_binary_', None) is None:
|
||||
self.skipTest("'_call_binary_' not defined.")
|
||||
|
||||
out = '\n'.join(self.run_script(self._call_binary_, "--version"))
|
||||
self.assertIn(self._call_binary_, out)
|
||||
self.assertIn(salt.__version__, out)
|
||||
|
|
28
tests/integration/shell/master.py
Normal file
28
tests/integration/shell/master.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
tests.integration.shell.master
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:copyright: © 2012 UfSoft.org - :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
# Import salt libs
|
||||
from saltunittest import TestLoader, TextTestRunner
|
||||
import integration
|
||||
from integration import TestDaemon
|
||||
|
||||
|
||||
class MasterTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
||||
|
||||
_call_binary_ = 'salt-master'
|
||||
|
||||
if __name__ == "__main__":
|
||||
loader = TestLoader()
|
||||
tests = loader.loadTestsFromTestCase(MasterTest)
|
||||
print('Setting up Salt daemons to execute tests')
|
||||
with TestDaemon():
|
||||
runner = TextTestRunner(verbosity=1).run(tests)
|
||||
sys.exit(runner.wasSuccessful())
|
28
tests/integration/shell/minion.py
Normal file
28
tests/integration/shell/minion.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
tests.integration.shell.minion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:copyright: © 2012 UfSoft.org - :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
# Import salt libs
|
||||
from saltunittest import TestLoader, TextTestRunner
|
||||
import integration
|
||||
from integration import TestDaemon
|
||||
|
||||
|
||||
class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
||||
|
||||
_call_binary_ = 'salt-minion'
|
||||
|
||||
if __name__ == "__main__":
|
||||
loader = TestLoader()
|
||||
tests = loader.loadTestsFromTestCase(MinionTest)
|
||||
print('Setting up Salt daemons to execute tests')
|
||||
with TestDaemon():
|
||||
runner = TextTestRunner(verbosity=1).run(tests)
|
||||
sys.exit(runner.wasSuccessful())
|
28
tests/integration/shell/syndic.py
Normal file
28
tests/integration/shell/syndic.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
tests.integration.shell.syndic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:copyright: © 2012 UfSoft.org - :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
# Import salt libs
|
||||
from saltunittest import TestLoader, TextTestRunner
|
||||
import integration
|
||||
from integration import TestDaemon
|
||||
|
||||
|
||||
class SyndicTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
||||
|
||||
_call_binary_ = 'salt-syndic'
|
||||
|
||||
if __name__ == "__main__":
|
||||
loader = TestLoader()
|
||||
tests = loader.loadTestsFromTestCase(SyndicTest)
|
||||
print('Setting up Salt daemons to execute tests')
|
||||
with TestDaemon():
|
||||
runner = TextTestRunner(verbosity=1).run(tests)
|
||||
sys.exit(runner.wasSuccessful())
|
Loading…
Add table
Reference in a new issue