因为/etc/salt下面好个配置文件,按照一般来说,master端应该只读取一个配置文件
/etc/salt/master
但是/etc/salt/下还有roster等文件不知道会不会读取,所以想干脆去瞅瞅源代码....
这一看就是个大坑
看了大半天saltstack,终于看明白了,到最后还是参考了
难点主要在于,saltsack的几个类初始化的使用使用python的元类
参考http://blog.jobbole.com/21351/
按照上面的说法
“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters
所以,所以要看懂saltstack,必须看下元类的大概作用,最后我还是参考了http://www.cnblogs.com/pping/p/3989699.html的文章才完全梳理清楚的
简单讲解下saltstack的启动过程
salt-master的init脚本就是调用/usr/bin/salt-master start
salt-master就是调用scripts.py里的salt_master方法
里面
master = salt.cli.daemons.Master()
下面坑就开始了,跟过去就看见
-
class MasterOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
-
LogLevelMixIn, RunUserMixin, DaemonMixIn,
-
PidfileMixin, SaltfileMixIn):
-
-
__metaclass__ = OptionParserMeta
-
-
description = 'The Salt master, used to control the Salt minions.'
-
-
# ConfigDirMixIn config filename attribute
-
_config_filename_ = 'master'
-
# LogLevelMixIn attributes
-
_default_logging_logfile_ = os.path.join(syspaths.LOGS_DIR, 'master')
-
-
def setup_config(self):
-
return config.master_config(self.get_config_file_path())
草...老子第一次看多重继承的方法....整个都傻了
当然,等大致都看明白的时候,就知道为什么要这么写了
这一堆类只有OptionParser直接有init方法,不明白元类之都看傻了都不知道这些类是干什么的
其实只要大致知道一点,就好理解了——python在init之前还有个__new__方法,这个都藏在每个类开始的__metaclass__里
操作__metaclass__同样可以初始化一些东西
MasterOptionParser类是
__metaclass__ = OptionParserMeta
OptionParser类没设置__metaclass__
其余类都是
__metaclass__ = MixInMeta
-
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__(mcs, name, bases, attrs):
-
instance = super(MixInMeta, mcs).__new__(mcs, 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(mcs.__name__, name)
-
)
-
return instance
看代码会发现,只要是__metaclass__ = MixInMeta都会有_mixin_setup方法
所以__metaclass__ = MixInMeta用来确保继承的类必须含有名为_mixin_setup的方法
我们再看有__metaclass__类MasterOptionParser
http://www.cnblogs.com/pping/p/3989704.html这里有详细点的注释
__metaclass__ = OptionParserMeta
-
class OptionParserMeta(MixInMeta):
-
def __new__(mcs, name, bases, attrs):
-
instance = super(OptionParserMeta, mcs).__new__(mcs,
-
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_
结合上面说的大概就明白了
MasterOptionParser的__metaclass__ = OptionParserMeta
会把ConfigDirMixIn, MergeConfigMixIn,LogLevelMixIn, RunUserMixin, DaemonMixIn,PidfileMixin, SaltfileMixIn
这几个类的
_mixin_setup函数塞入_mixin_setup_funcs里,也就是MasterOptionParser._mixin_setup_funcs
_mixin_process_funcs函数塞入_mixin_process_funcs里,也就是MasterOptionParser._mixin_process_funcs
_mixin_after_parsed_funcs函数塞入_mixin_after_parsed_funcs里,也就是MasterOptionParser._mixin_after_parsed_funcs
于是当master=MasterOptionParser()的时候
master._mixin_setup/_mixin_process_funcs/_mixin_after_parsed_funcs里就有好多个函数了
上个测试代码看看
-
import salt
-
from salt.utils import parsers, print_cli
-
-
class Stest(parsers.OptionParser,parsers.TimeoutMixIn,parsers.ConfigDirMixIn):
-
__metaclass__ = parsers.OptionParserMeta
-
default_timeout = 10
-
-
-
loli = Stest()
-
-
print len(loli._mixin_setup_funcs)
-
print loli._mixin_setup
结果是
2
>
再来看没有自定义__metaclass__的OptionParser类
msater类的init方法就在OptionParser里,OptionParser就是我们常用的继承类
OptionParser继承自python库optarse.OptionParser(optarse个库是用来处理参数的)
master类调用了自己的init方法以后,还调用了基类的init方法
optparse.OptionParser.__init__(self, *args, **kwargs)
optparse.OptionParser的init方法里有调用
self._populate_option_list(option_list,add_help=add_help_option)
由于master是继承自parsers.OptionParser的类
所以实际的_populate_option_list不是optarse.OptionParser里的,而是
master自己(parsers.OptionParser)的_populate_option_list
这里调用了optarse.OptionParser里原来的同名函数后再调用所有_mixin_setup函数
-
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)
看到这里我我们就知道为什么要继承那么多类了
salt的各类对象继承不同的几个类类封装成对应的类
这样继承相当工厂化,几个零件拼在一起就成为一个新对象
元类是为了方便初始化,不然init里对应不同的封装会不好写成一样的代码。
master类创建好以后
master守护进程启动前会先调用parse_args函数
读取配置文件就在parse_args里
-
for option_key in options.__dict__:
-
process_option_func = getattr(
-
self, 'process_{0}'.format(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):
-
try:
-
process_option_func()
-
except Exception as err:
-
logging.getLogger(__name__).exception(err)
-
self.error(
-
'Error while processing {0}: {1}'.format(
-
process_option_func, traceback.format_exc(err)
-
)
-
)
options.__dict__就是"config_dir"、"log_level"、"log_file"(字符串内容来源好找就不跟过去了)
所以上面就是分别执行process_config_dir、process_log_level、process_log_file
process_config_dir就在class ConfigDirMixIn里
-
class ConfigDirMixIn(object):
-
__metaclass__ = MixInMeta
-
_mixin_prio_ = -10
-
_config_filename_ = None
-
-
def _mixin_setup(self):
-
config_dir = os.environ.get('SALT_CONFIG_DIR', None)
-
if not config_dir:
-
config_dir = syspaths.CONFIG_DIR
-
self.add_option(
-
'-c', '--config-dir', default=config_dir,
-
help=('Pass in an alternative configuration directory. Default: '
-
'%default')
-
)
-
-
def process_config_dir(self):
-
if not os.path.isdir(self.options.config_dir):
-
# No logging is configured yet
-
sys.stderr.write(
-
'WARNING: {0!r} directory does not exist.\n'.format(
-
self.options.config_dir
-
)
-
)
-
-
# Make sure we have an absolute path
-
self.options.config_dir = os.path.abspath(self.options.config_dir)
-
-
if hasattr(self, 'setup_config'):
-
if not hasattr(self, 'config'):
-
self.config = {}
-
try:
-
self.config.update(self.setup_config())
-
except (IOError, OSError) as exc:
-
self.error(
-
'Failed to load configuration: {0}'.format(exc)
-
)
-
-
def get_config_file_path(self, configfile=None):
-
if configfile is None:
-
configfile = self._config_filename_
-
return os.path.join(self.options.config_dir, configfile)
self.config = {}这个就是配置文件你内容存放的字典
然后self.config.update(self.setup_config())
setup_config又直接在class MasterOptionParser,看前面代码
-
def setup_config(self):
-
return config.master_config(self.get_config_file_path())
get_config_file_path()又在class ConfigDirMixIn里(有点绕)
最终config.master_config获取到了文件路径,解析后
self.config这个字典里就存有了配置文件的内容
master类会更具self.config的内容做一些初始化后,并根据配置文件生产一个也叫master的实例
zmq的用
self.master = salt.master.Master(self.config)
新的ret上面的用
self.master = salt.daemons.flo.IofloMaster(self.config)
实例生成后
通过self.daemonize_if_required()
-
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()
成为守护进程,启动完毕
守护进程salt.utils.daemonize()的代码比较简单,就fork两次,默认把0、1、2 重定向到/dev/null
这里就不贴了
看懂这个其实还是很有意义的
salt的类都是在这个类工厂里封装的
比如说,默认超时时间是5秒,这个体现在代码的哪里.
默认超时时间就在TimeoutMixIn里,大家又兴趣可以自己看
阅读(1543) | 评论(0) | 转发(0) |