nova reads the configuration file Process

Original link: http://www.cnblogs.com/james1207/p/3366121.html

      We are in the process of installing nova, set its configuration file /etc/nova/nova.conf is an essential step. Configured nova.conf file, nova-compute, nova-network services possible start properly. Of course, after modifying nova.conf file, all services must be restarted nova, the new configuration to take effect.
      In fact, we do not have to store the configuration file for the /etc/nova/nova.conf, it can be set to any files in any directory. There are two ways nova obtain the configuration file:
1, the command line option --config-file to specify any file as a configuration file (provided there is of course read and write permissions) .
For example: / usr / bin / python /usr/bin/nova-compute--config-file=/etc/nova/nova.conf--config-file=/etc/nova/nova-compute.conf
      By --config-file option, you can specify any number of profiles on patients to nova-compute service specifies two configuration files. In this case, these two items in the configuration file settings, are effective. But if these two files have the same configuration items, configuration items and this is not allowed to have more than one value, it will be as effective which refers to it, or errors occur?
2. If at the time the service starts, do not specify a configuration file --config-file, then nova looks for configuration files in accordance with the following order of several specific directory. When each service starts, it looks for configuration files in the following order:
[~ / .New / ~ /, / etc / new /, / etc /]
For example, when you start nova-compute, respectively, it will look for nova.conf and nova-compute.conf in these directories, if ~ / .nova / nova.conf exist, then it will not find in other directories nova.conf a. The same is true for nova-compute.conf file.

nova configuration file content format is as follows:
[DEFAULT]
dhcpbridge_flagfile = / etc / new / nova.conf
dhcpbridge=/usr/bin/nova-dhcpbridge
libvirt_use_virtio_for_bridges=True
connection_type=libvirt
...…
analysis

    Nova components for analysis below is how to read the command line and configuration files, from the analysis process, we can know:
1) nova is how to find the configuration files, configuration files can be read in the directory in which it
2) edit the configuration file should follow what kind of format, in order to be properly read nova
3) When we need to add one of our own options in the configuration file, we should be how to modify the areas in which, in order to be nova to get the value we write the configuration file

nova multiple services: nova-CERT, nova-consoleauth, the Manage-nova, nova-novncproxy, nova-Scheduler, nova-API, Compute-nova, nova-dhcpbridge, nova-Network, nova-rootwrap. Individual services at startup, read the configuration file of the process is the same. The following analysis of the reading process for nova-compute configuration is the same as other services.

compute startup script
/ Usr / bin / nova-compute file:
  42 if __name__ == '__main__':
  43     flags.parse_args(sys.argv)
  44     logging.setup('nova')
  45     utils.monkey_patch()
  46     server = service.Service.create(binary='nova-compute')
  47     service.serve(server)
  48     service.wait()

Line 42 calls flags.parse_args (sys.argv) function, which is defined in nova / flags.py. It calls CommonConfigOpts instance read command-line parameters and configuration file:
nova/flags.py:
  36 FLAGS = cfg.CONF
  37 
  38 
  39 def parse_args(argv,default_config_files=None):
  40     FLAGS.disable_interspersed_args()
  41     return argv[:1] + FLAGS(argv[1:],
  42                             project='nova',
  43                             default_config_files=default_config_files)

继续查看cfg.CONF
/nova/openstack/common/cfg.py
1654  CONF =CommonConfigOpts()

    由上面代码可知,nova-compute后面的命令行参数直接调用CommonConfigOpts对象处理,并设project=‘nova’,默认配置文件为空。 CommonConfigOpts乃何许类也,它都干些什么呢?
      CommonConfigOpts是ConfigOpts的子类,而在ConfigOpts中定义__call__函数,所以CommonConfigOpts对象可以像函数一样,被直接调用。为了能够理解这两个类是干什么的,我们需要先了解另外几个类:

optparse.OptionParser
optparser库中的OptionParser,用来解析通过命令行指定的参数,该库目前不再更新,将会被argparser代替。但openstack使用该库来解析,所以做个简单介绍:

from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",#dest用来明确指定解析参数后对应的属性名,
                                                                                  #如果没有指定则为file
                  help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose", default=True,
                  help="don't print status messages to stdout")

(options, args) = parser.parse_args(['-f', 'test.txt', 'ddd','ffff'])
该脚本执行过后,options.filename=test.txt、options.verbose=True。args=[ 'ddd','ffff'],它是用于返回不能被OptionParser识别的参数。

optparse.OptionGroup
当参数较多时,可利用OptionGroup对其进行分类,这样在输出help信息时,能够分类整齐些,所以每一个OptionGroup都必须与一个OptionParser关联(多对一的关系)。在使用上,与OptionParser差不多。如下:
group = OptionGroup(parser, "Dangerous Options",#其中parser为上面定义的类
                    "Caution: use these options at your own risk.  "
                    "It is believed that some of them bite.")
group.add_option("-g", action="store_true", help="Groupoption.")
parser.add_option_group(group)

(options,args) = parser.parse_args(['-f', 'test.txt', 'ddd','ffff'])
该option和args的值与上例中结果一样。

openstack.common.cfg.Opt:
        self.name = name   #name对应命令行参数名和配置文件中的配置项名
          self.dest = dest #解析命令行参数时生成属性的名,通过该名可获取命令行参数值,如上例filename
        self.short = short  #命令行参数的简写,如上例中f
        self.default = default  #该选项的默认值
        self.metavar = metavar  #用于在显示帮助信息时用的,如FILE
        self.help = help
        self.secret = secret     #bool类型,指定该选项是否为保密信息,这样在打印日志时会用特殊字符代替
        self.required = required#是否为必填项,在读取配置文件后,会依据该值进行检查
        self.deprecated_name = None  #该选项的备用名,可用作命令行参数名和配置项名

ConfigParser(iniparser.BaseParser):
        self.filename = filename
        self.sections = sections

    def parse(self):
        with open(self.filename) as f:
            returnsuper(ConfigParser, self).parse(f)

对于每一个配置文件,都会生成一个ConfigParser对象。该类调用BaseParser.parse()函数,将文件内容解析成keyvalue对,定义如下:
  def parse(self, lineiter):
        key = None
        value = []

        for line in lineiter:
            self.lineno += 1

            line =line.rstrip()
            if notline:
                # Blank line, ends multi-linevalues
                if key:
                    key, value = self._assignment(key, value)
                continue
            elifline[0] in (' ', '\t'):
                # Continuation of previousassignment,这里可以知道,为什么配置项不能以空格开始了
                if key is None:
                    self.error_unexpected_continuation(line)
                else:
                    value.append(line.lstrip())
                continue

            ifkey:
                # Flush previous assignment,if any
                key, value =self._assignment(key, value)

            if line[0]== '[':
                # Section start
                section =self._get_section(line)
                if section:
                    self.new_section(section)
            elifline[0] in '#;':     #这里可以看出配置项的key和value可以通过#或;进行分隔
                self.comment(line[1:].lstrip())
            else:
                key, value =self._split_key_value(line)
                if not key:
                    return self.error_empty_key(line)
        if key:#用作处理最后一个key value对
            # Flushprevious assignment, if any
            self._assignment(key, value)

从上面的代码可知,ConfigParser读取配置文件,将每个配置项以{key:value}储存在sections中{‘section1’:{‘key11’:value}}。在配件文件中,每个配置项必须处于一个section之中,即在一个通过[section]定义的行之下。否则会报parse_exc异常。这样,一个配置文件中所有配置项都被读到了ConfigParser的sections中。

classMultiConfigParser(object):
    def__init__(self):
        self.parsed = []

    def read(self,config_files):
        read_ok = []

        for filename in config_files:
            sections ={}
            parser =ConfigParser(filename, sections)

            try:
                parser.parse()
            exceptIOError:
                continue
            self.parsed.insert(0, sections)
            read_ok.append(filename)

        return read_ok

    def get(self, section,names, multi=False):
        rvalue = []
        for sections in self.parsed:
            if sectionnot in sections:
                continue
            for namein names:
                if name insections[section]:
                    if multi:
                        rvalue =sections[section][name] + rvalue
                    else:
                        returnsections[section][name]
        if multi and rvalue != []:
            returnrvalue
        raise KeyError
该类的read函数,为每个配置文件初始化一个ConfigPaser对象,并将该对象解析过后的sections存放到parsed只中,这样每个配置文件就对应了parsed列表中的一项,所有配置文件的配置项都存放在parsed成员变量中。
      这里需要注意一点,在往parsed中添加sections时,使用 parsed.insert(0,sections)。排在后面的配置文件,解析后会放在parsed的最前面。当调用get()获取配置项的时,如果该配置项不为多选项(multi),那么只会去parsed前面sections中的值。也就是说如果一个配置项在多个配置文件中被重复定义,那么只会读取最后一个配置文件中的值作为使用值。


      好了,在了解了上面几个类之后,我们可以查看ConfigOpts和CommonConfigOpts类了。ConfigOpts的成员变量如下:
ConfigOpts(collections.Mapping):
/nova/openstack/common/cfg.py 
    def__init__(self):
        """Construct a ConfigOpts object."""
        self._opts = {}   # dict ofdicts of (opt:, override:, default:)
        self._groups = {}

        self._args = None
        self._oparser = None  #命令行参数解释器,即一个OptionParser对象
        self._cparser = None  #配置文件解释器,即一个MultiConfigParser对象
        self._cli_values = {}     #程序启动时,通过_oparser解释的命令行参数值
        self.__cache = {}         #缓存
        self._config_opts = []  
        self._disable_interspersed_args = False

   def __call__(self,
              args=None,
              project=None,
              prog=None,
              version=None,
              usage=None,
             default_config_files=None):

      self.clear()

      self._setup(project, prog, version, usage,default_config_files)

      self._cli_values, leftovers =self._parse_cli_opts(args)

      self._parse_config_files()

      self._check_required_opts()

       returnleftovers


clear()函数:
清空opts、groups、cache等成员变量,对于刚初始化的ConfigOpts对象,基本什么也没干。

_setup函数:
      def_setup(self, project, prog, version, usage,default_config_files):
        """Initialize a ConfigOpts object for optionparsing."""
        if prog is None:
            prog =os.path.basename(sys.argv[0])

        if default_config_files is None:
            #在['~/.nova/', '~/', '/etc/nova/','/etc/']寻找配置文件nova.conf和nova-compute.conf
            default_config_files = find_config_files(project, prog)

        self._oparser =optparse.OptionParser(prog=prog,
                                              version=version,
                                              usage=usage)
        if self._disable_interspersed_args:
            self._oparser.disable_interspersed_args()

        self._config_opts = [
            MultiStrOpt('config-file',
                        default=default_config_files,
                        metavar='PATH',
                        help='Pathto a config file to use. Multiple config '
                              'files canbe specified, with values in later '
                              'filestaking precedence. The default files '
                              ' used are:%s' % (default_config_files, )),
            StrOpt('config-dir',
                    metavar='DIR',
                    help='Path to a config directory to pull *.conf'
                        'filesfrom. This file set is sorted, so as to '
                        'provide apredictable parse order if individual '
                        'optionsare over-ridden. The set is parsed after '
                        'thefile(s), if any, specified via --config-file, '
                        'henceover-ridden options in the directory take '
                        'precedence.'),
        ]
        #注册命令行参数config-file和config-dir,这样就可以通过命令行指定该配置项了。
        self.register_cli_opts(self._config_opts)

        self.project = project
        self.prog = prog
        self.version = version
        self.usage = usage
        self.default_config_files =default_config_files
1、寻找配置文件,如果__call__的参数default_config_files=None。那么它将按一下顺序~/.nova/,~/, /etc/nova/,/etc/搜索配置文件nova.conf和nova-compute.conf,对于每个配置文件只要在一个目录下搜索到,剩余的目录就不会被搜索。并将default_config_files赋值为搜索的结果,
2、用命令行参数config-file、config-dir初始化命令行解释器:
      self._oparser =optparse.OptionParser(prog=prog, version=version,usage=usage)
这样就能解析通过命令行传递参数config-file、config-dir的值。

_parse_cli_opts函数
用于解析通过命令行传过来的参数:它先调用Opt._add_to_cli将所有的opts全都注册到self._oparser中。再调用self._oparser.parse_args(args)解析所有的命令函数。然后将解析结果存放到self._cli_values中。
此时命令行参数已经解析完成,然后解析配置文件中的选项。

_parse_config_files函数:
        """Parse the config files from --config-file and--config-dir.

        :raises: ConfigFilesNotFoundError ,ConfigFileParseError
        """
        config_files = list(self.config_file)
     
        if self.config_dir:  #如果指定了config_dir,那么该目录下所有匹配*.conf的文件,都会被当作配置文件
            config_dir_glob = os.path.join(self.config_dir, '*.conf')
            config_files += sorted(glob.glob(config_dir_glob))

        config_files = [_fixpath(p) for p inconfig_files]

        self._cparser = MultiConfigParser()

        try:
            read_ok =self._cparser.read(config_files)
        except iniparser.ParseError as pe:
            raiseConfigFileParseError(pe.filename, str(pe))
         
        if read_ok != config_files:
            not_read_ok = filter(lambda f: f not in read_ok,config_files)
            raiseConfigFilesNotFoundError (not_read_ok)

在这个函数中,self._cparser被初始化:
self._cparser =MultiConfigParser()
如果通过命令行参数--config-file指定配置文件,和--config-dir指定配置目录。那这些文件全都会通过
read_ok =self._cparser.read(config_files)
解析后的选项和值都被存放到列表self._cparser.parsed中,其中每一元素对应一个配置文件,元素结构是:
sections[section][key]=value

_check_required_opts函数:
根据每个opt的requred属性,判断是否所有必设置选项是否被设置,如果没有,则报异常。

classCommonConfigOpts(ConfigOpts):
CommonConfigOpts只是重新定义了 构造函数,在初始化对象时,注册了几个Opt对象:-d\-v\--log-config\--log-format\--log-date-format\--log-file\--log-dir\--use-syslog\--syslog-log-facility。这样就可以通过命令行参数对这些配置项进行设置。
到此,命令行参数和配置文件参数都已解析完成,所有的命令行参数和配置文件内容都保存到FLAGS中。


获取配置项的值
  def _do_get(self, name, group=None):
        if group is None and name in self._groups:
            returnself.GroupAttr(self, self._get_group(name))

        #首先要获取该对象的注册信息,所以使用前必须先注册
        info = self._get_opt_info(name, group)
        opt = info['opt']

        if 'override' in info:
            returninfo['override']

        values = []
        if self._cparser is not None:
            section =group.name if group is not None else 'DEFAULT'
            try:
                value =opt._get_from_config_parser(self._cparser, section)
            exceptKeyError:
                pass
            exceptValueError as ve:
                raiseConfigFileValueError(str(ve))
            else:
                if not opt.multi:
                    # No need to continue since the last valuewins
                    return value[-1]
                values.extend(value)

        name = name if group is None else group.name +'_' + name
        value = self._cli_values.get(name)
        if value is not None:
            if notopt.multi:
                return value

            returnvalue + values

        if values:
            returnvalues

        if 'default' in info:
            returninfo['default']  

      returnopt.default
从该流程中,可以看出。获取一个配置项的值,首先得注册该配置项。获取该配置项的值的优先级如下:
info['override'],配置文件,命令行参数、info['default']、Opt['default']

注册一个配置项流程如下:
compute_opts = [
    cfg.StrOpt('instances_path',
                default='$state_path/instances',
                help='where instances arestored on disk'),
    cfg.IntOpt('live_migration_retry_count',
                default=30,
                help="Number of 1 secondretries needed in live_migration"),
]
FLAGS = flags.FLAGS
FLAGS.register_opts(compute_opts)
然后就可以如下进行访问了
retry = FLAGS. live_migration_retry_count

StrOpt、IntOpt、BoolOpt、FloatOpt、ListOpt是Opt的子类,它们主要是重定义了获取值和注册OptionParser的函数,以IntOpt为例:
class IntOpt(Opt):

    """Int opt values areconverted to integers using the int() builtin."""

    def_get_from_config_parser(self, cparser, section):
        """Retrieve the opt value as a integer fromConfigParser."""
        return [int(v) for v inself._cparser_get_with_deprecated(cparser,
                section)]

    def_get_optparse_kwargs(self, group, **kwargs):
        """Extends the base optparse keyword dict forinteger options."""
        return super(IntOpt,
                      self)._get_optparse_kwargs(group, type='int',**kwargs)

这样,特定类型的Opt在获取配置文件中的值和通过命令行参数获得的值,都能够自动转换成相应的类型,方便使用。

 

转载于:https://www.cnblogs.com/james1207/p/3366121.html

Guess you like

Origin blog.csdn.net/weixin_30470643/article/details/94984872