Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1101972
  • 博文数量: 170
  • 博客积分: 1603
  • 博客等级: 上尉
  • 技术积分: 1897
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-09 15:54
文章分类

全部博文(170)

文章存档

2016年(27)

2015年(21)

2014年(27)

2013年(21)

2012年(7)

2011年(67)

我的朋友

分类: Python/Ruby

2015-01-03 17:35:14

因为/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()

下面坑就开始了,跟过去就看见


点击(此处)折叠或打开

  1. class MasterOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
  2.                          LogLevelMixIn, RunUserMixin, DaemonMixIn,
  3.                          PidfileMixin, SaltfileMixIn):

  4.     __metaclass__ = OptionParserMeta

  5.     description = 'The Salt master, used to control the Salt minions.'

  6.     # ConfigDirMixIn config filename attribute
  7.     _config_filename_ = 'master'
  8.     # LogLevelMixIn attributes
  9.     _default_logging_logfile_ = os.path.join(syspaths.LOGS_DIR, 'master')

  10.     def setup_config(self):
  11.         return config.master_config(self.get_config_file_path())


草...老子第一次看多重继承的方法....整个都傻了
当然,等大致都看明白的时候,就知道为什么要这么写了

这一堆类只有OptionParser直接有init方法,不明白元类之都看傻了都不知道这些类是干什么的
其实只要大致知道一点,就好理解了——python在init之前还有个__new__方法,这个都藏在每个类开始的__metaclass__里
操作__metaclass__同样可以初始化一些东西


MasterOptionParser类是
__metaclass__ = OptionParserMeta

OptionParser类没设置__metaclass__

其余类都是
__metaclass__ = MixInMeta


点击(此处)折叠或打开

  1. class MixInMeta(type):
  2.     # This attribute here won't actually do anything. But, if you need to
  3.     # specify an order or a dependency within the mix-ins, please define the
  4.     # attribute on your own MixIn
  5.     _mixin_prio_ = 0

  6.     def __new__(mcs, name, bases, attrs):
  7.         instance = super(MixInMeta, mcs).__new__(mcs, name, bases, attrs)
  8.         if not hasattr(instance, '_mixin_setup'):
  9.             raise RuntimeError(
  10.                 'Don\'t subclass {0} in {1} if you\'re not going to use it '
  11.                 'as a salt parser mix-in.'.format(mcs.__name__, name)
  12.             )
  13.         return instance


看代码会发现,只要是__metaclass__ = MixInMeta都会有_mixin_setup方法
所以__metaclass__ = MixInMeta用来确保继承的类必须含有名为_mixin_setup的方法

我们再看有__metaclass__类MasterOptionParser
http://www.cnblogs.com/pping/p/3989704.html这里有详细点的注释
__metaclass__ = OptionParserMeta


点击(此处)折叠或打开

  1. class OptionParserMeta(MixInMeta):
  2.     def __new__(mcs, name, bases, attrs):
  3.         instance = super(OptionParserMeta, mcs).__new__(mcs,
  4.                                                         name,
  5.                                                         bases,
  6.                                                         attrs)
  7.         if not hasattr(instance, '_mixin_setup_funcs'):
  8.             instance._mixin_setup_funcs = []
  9.         if not hasattr(instance, '_mixin_process_funcs'):
  10.             instance._mixin_process_funcs = []
  11.         if not hasattr(instance, '_mixin_after_parsed_funcs'):
  12.             instance._mixin_after_parsed_funcs = []

  13.         for base in _sorted(bases + (instance,)):
  14.             func = getattr(base, '_mixin_setup', None)
  15.             if func is not None and func not in instance._mixin_setup_funcs:
  16.                 instance._mixin_setup_funcs.append(func)

  17.             func = getattr(base, '_mixin_after_parsed', None)
  18.             if func is not None and func not in \
  19.                     instance._mixin_after_parsed_funcs:
  20.                 instance._mixin_after_parsed_funcs.append(func)

  21.             # Mark process_<opt> functions with the base priority for sorting
  22.             for func in dir(base):
  23.                 if not func.startswith('process_'):
  24.                     continue

  25.                 func = getattr(base, func)
  26.                 if getattr(func, '_mixin_prio_', None) is not None:
  27.                     # Function already has the attribute set, don't override it
  28.                     continue

  29.                 func.__func__._mixin_prio_ = getattr(
  30.                     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里就有好多个函数了
上个测试代码看看

点击(此处)折叠或打开

  1. import salt
  2. from salt.utils import parsers, print_cli

  3. class Stest(parsers.OptionParser,parsers.TimeoutMixIn,parsers.ConfigDirMixIn):
  4.      __metaclass__ = parsers.OptionParserMeta
  5.      default_timeout = 10


  6. loli = Stest()

  7. print len(loli._mixin_setup_funcs)
  8. 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函数

点击(此处)折叠或打开

  1. def _populate_option_list(self, option_list, add_help=True):
  2.         optparse.OptionParser._populate_option_list(
  3.             self, option_list, add_help=add_help
  4.         )
  5.         for mixin_setup_func in self._mixin_setup_funcs:
  6.             mixin_setup_func(self)
看到这里我我们就知道为什么要继承那么多类了
salt的各类对象继承不同的几个类类封装成对应的类
这样继承相当工厂化,几个零件拼在一起就成为一个新对象
元类是为了方便初始化,不然init里对应不同的封装会不好写成一样的代码。

master类创建好以后

master守护进程启动前会先调用parse_args函数


读取配置文件就在parse_args里


点击(此处)折叠或打开

  1. for option_key in options.__dict__:
  2.     process_option_func = getattr(
  3.         self, 'process_{0}'.format(option_key), None
  4.     )
  5.     if process_option_func is not None:
  6.         process_option_funcs.append(process_option_func)

  7. for process_option_func in _sorted(process_option_funcs):
  8.     try:
  9.         process_option_func()
  10.     except Exception as err:
  11.         logging.getLogger(__name__).exception(err)
  12.         self.error(
  13.             'Error while processing {0}: {1}'.format(
  14.                 process_option_func, traceback.format_exc(err)
  15.             )
  16.         )


options.__dict__就是"config_dir"、"log_level"、"log_file"(字符串内容来源好找就不跟过去了)
所以上面就是分别执行process_config_dir、process_log_level、process_log_file 
process_config_dir就在class ConfigDirMixIn里


点击(此处)折叠或打开

  1. class ConfigDirMixIn(object):
  2.     __metaclass__ = MixInMeta
  3.     _mixin_prio_ = -10
  4.     _config_filename_ = None

  5.     def _mixin_setup(self):
  6.         config_dir = os.environ.get('SALT_CONFIG_DIR', None)
  7.         if not config_dir:
  8.             config_dir = syspaths.CONFIG_DIR
  9.         self.add_option(
  10.             '-c', '--config-dir', default=config_dir,
  11.             help=('Pass in an alternative configuration directory. Default: '
  12.                   '%default')
  13.         )

  14.     def process_config_dir(self):
  15.         if not os.path.isdir(self.options.config_dir):
  16.             # No logging is configured yet
  17.             sys.stderr.write(
  18.                 'WARNING: {0!r} directory does not exist.\n'.format(
  19.                     self.options.config_dir
  20.                 )
  21.             )

  22.         # Make sure we have an absolute path
  23.         self.options.config_dir = os.path.abspath(self.options.config_dir)

  24.         if hasattr(self, 'setup_config'):
  25.             if not hasattr(self, 'config'):
  26.                 self.config = {}
  27.             try:
  28.                 self.config.update(self.setup_config())
  29.             except (IOError, OSError) as exc:
  30.                 self.error(
  31.                     'Failed to load configuration: {0}'.format(exc)
  32.                 )

  33.     def get_config_file_path(self, configfile=None):
  34.         if configfile is None:
  35.             configfile = self._config_filename_
  36.         return os.path.join(self.options.config_dir, configfile)



self.config = {}这个就是配置文件你内容存放的字典
然后self.config.update(self.setup_config())
setup_config又直接在class MasterOptionParser,看前面代码

点击(此处)折叠或打开

  1. def setup_config(self):
  2.     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()

点击(此处)折叠或打开

  1. class DaemonMixIn(object):
  2.     __metaclass__ = MixInMeta
  3.     _mixin_prio_ = 30

  4.     def _mixin_setup(self):
  5.         self.add_option(
  6.             '-d', '--daemon',
  7.             default=False,
  8.             action='store_true',
  9.             help='Run the {0} as a daemon'.format(self.get_prog_name())
  10.         )

  11.     def daemonize_if_required(self):
  12.         if self.options.daemon:
  13.             # Late import so logging works correctly
  14.             import salt.utils
  15.             salt.utils.daemonize()

成为守护进程,启动完毕
守护进程salt.utils.daemonize()的代码比较简单,就fork两次,默认把0、1、2 重定向到/dev/null
这里就不贴了



看懂这个其实还是很有意义的
salt的类都是在这个类工厂里封装的
比如说,默认超时时间是5秒,这个体现在代码的哪里.
默认超时时间就在TimeoutMixIn里,大家又兴趣可以自己看



阅读(1464) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~