服务器配置文件salt,源码分析saltsack的配置文件读取过程

因为/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_ 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里,大家又兴趣可以自己看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值