新星は、設定ファイルのプロセスを読み込みます

オリジナルリンク: http://www.cnblogs.com/james1207/p/3366121.html

      私たちは、新星をインストールするプロセスであり、その構成ファイル/etc/nova/nova.confが不可欠なステップである設定しました。構成されたnova.confファイル、新星・コンピューティング、可能新星・ネットワーク・サービスが正常に開始します。もちろん、nova.confファイルを変更した後、すべてのサービスが新星、有効にする新しい設定を再起動する必要があります。
      実際には、我々は/etc/nova/nova.conf用の設定ファイルを保存する必要はありません、それは任意のディレクトリ内のすべてのファイルに設定することができます。新星設定ファイルを入手方法は2つあります。
図1は、コマンドライン オプション --configファイル を指定する 構成ファイルなどの任意のファイルは、(読み取りおよび書き込み権限もちろんが提供されます)
たとえば、次のようには/ usr / binに/ pythonの/usr/bin/nova-compute--config-file=/etc/nova/nova.conf--config-file=/etc/nova/nova-compute.conf
      --config-fileオプションによって、あなたは新星・コンピューティング・サービスへの患者上のプロファイルの任意の数を指定することができますは、2つの設定ファイルを指定します。この場合は、設定ファイルの設定でこれらの2つの項目が、効果的です。これら2つのファイルが同じ設定項目、設定項目があり、これは複数の値を持つことが許可されていない場合でも、それを意味しているとして有効であるか、またはエラーが発生しますか?
サービスの開始時に、コンフィギュレーション・ファイル--config-ファイルを指定しない2.場合は、新星は、いくつかの特定のディレクトリの次の順序に従って、設定ファイルを探します。各サービスは、起動時に次の順序で設定ファイルを探します。
[〜/ .New /〜/は、/ etc /新/は、/ etc /]
たとえば、ときあなたは新星・コンピューティングを開始し、それぞれ、それは〜/ .nova / nova.confが存在する場合、それは他のディレクトリのnova.confに見つけることができません、これらのディレクトリにnova.confおよびNova-compute.confを探しますA。同じことは新星・compute.confファイルについても同様です。

次のように新星設定ファイルの内容の形式は次のとおりです。
[デフォルト]
dhcpbridge_flagfile =の/ etc /新/ nova.conf
dhcpbridge =は/ usr / binに/新星、dhcpbridge
libvirt_use_virtio_for_bridges =真
CONNECTION_TYPE = libvirtを
... ...
分析

    分析のためのノヴァ・コンポーネントは、以下の分析プロセスから、私たちが知ることができ、コマンドラインと設定ファイルを読み込む方法です。
1)新星は、設定ファイルがでてディレクトリに読み取ることができ、コンフィギュレーション・ファイルを検索する方法です
2)従うべき設定ファイルを編集フォーマットの種類、適切に新星を読み取ることするために、
我々は、設定ファイルで私たち自身のいずれかのオプションを追加する必要がある場合3)、我々は、設定ファイルを記述した値を取得するために新星をするためには、地域で変更する方法でなければなりません

新星複数のサービス:新星-CERT、新星-consoleauth、管理-新星、新星-novncproxy、新星・スケジューラ、 新星-API、計算-新星、新星-dhcpbridge、新星・ネットワーク、新星-rootwrap。起動時に個々のサービスは、プロセスの設定ファイルが同じで読みました。新星・コンピューティング構成の読み取りプロセスの次の分析は、他のサービスと同じです。

計算スタートアップスクリプト
/ usr / binに/新星-計算ファイル:
  42もし__name__ == '__main__':
  43 flags.parse_args(sys.argvの)
  44 logging.setup( 'ノヴァ')
  45 utils.monkey_patch()
  46サーバ= service.Service.create(バイナリ= '新星-計算')
  47 service.serve(サーバー)
  48 service.wait()

新星/ flags.pyで定義されているライン42通話flags.parse_args(sys.argvの)関数、。それはCommonConfigOptsインスタンスはコマンドラインパラメータや設定ファイルを読んで呼び出します。
新星/ 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

おすすめ

転載: blog.csdn.net/weixin_30470643/article/details/94984872