返回介绍

OpenStack 模块代码结构

发布于 2025-02-18 00:20:47 字数 17118 浏览 0 评论 0 收藏 0

1.简介

在开始介绍各个 OpenStack 服务的 Puppet 模块前,先观察一下所有 OpenStack module 的目录结构,你会发现所有的模块的部分代码目录结构和命名方式几乎是一致的,这是经过了长期迭代和开发中形成的规范和统一,代码结构统一带来的好处有两点:

  1. 易于维护人员理解和管理
  2. 减少冗余代码,提高代码复用

那么我们就来看看一个 OpenStack 服务的 Module 中包含了哪些目录:

  • examples/ 放置示例代码
  • ext/ 放置 external 代码,和主要代码无关,但是一些有用的脚本
  • lib/ 放置 library 代码,例如自定义 facter,resource type
  • manifests/ 放置 puppet 代码
  • releasenotes/ 放置 releasenote
  • spec/ 放置 class ,unit,acceptance 测试
  • tests/ 已弃用,使用 examples 替代

以上目录中最重要的是 manifests 目录,用于放置 Puppet 代码,在该目录下包含了以下通用代码文件:

名称说明
init.pp主类,也称为入口类,通常仅用于管理公共参数(如 MQ 参数)
params.pp用于特定操作系统的参数值设置
client.pp管理客户端的配置
config.pp用于管理自定义的参数配置
policy.pppolicy 设置
db/支持多种数据库后端的配置
keystone/keystone endpoint,service,user,role 的设置

2.数据库管理

2.1 class <service>::db

class <service>::db 用于管理各 OpenStack 服务中的数据库相关配置, <service> 是 OpenStack 服务的名称,以 Aodh 为例:

class aodh::db (
  $database_db_max_retries = $::os_service_default,
  $database_connection     = 'sqlite:////var/lib/aodh/aodh.sqlite',
  $database_idle_timeout   = $::os_service_default,
  $database_min_pool_size  = $::os_service_default,
  $database_max_pool_size  = $::os_service_default,
  $database_max_retries    = $::os_service_default,
  $database_retry_interval = $::os_service_default,
  $database_max_overflow   = $::os_service_default,
) {

  include ::aodh::deps

  $database_connection_real = pick($::aodh::database_connection, $database_connection)
  $database_idle_timeout_real = pick($::aodh::database_idle_timeout, $database_idle_timeout)
  $database_min_pool_size_real = pick($::aodh::database_min_pool_size, $database_min_pool_size)
  $database_max_pool_size_real = pick($::aodh::database_max_pool_size, $database_max_pool_size)
  $database_max_retries_real = pick($::aodh::database_max_retries, $database_max_retries)
  $database_retry_interval_real = pick($::aodh::database_retry_interval, $database_retry_interval)
  $database_max_overflow_real = pick($::aodh::database_max_overflow, $database_max_overflow)

  oslo::db { 'aodh_config':
    db_max_retries => $database_db_max_retries,
    connection     => $database_connection_real,
    idle_timeout   => $database_idle_timeout_real,
    min_pool_size  => $database_min_pool_size_real,
    max_pool_size  => $database_max_pool_size_real,
    max_retries    => $database_max_retries_real,
    retry_interval => $database_retry_interval_real,
    max_overflow   => $database_max_overflow_real,
  }
}

class aodh::db 管理了与数据库相关的配置项,其中通过调用 oslo::db 来实现,关于 puppet-oslo 模块,本书会在下一章节详细说明。

2.2 class <service>::db::mysql

class <service>::db::mysql 用于创建相关服务的 MySQL 数据库,用户和授权等。以 Aodh 为例:

class aodh::db::mysql(
  $password,
  $dbname        = 'aodh',
  $user          = 'aodh',
  $host          = '127.0.0.1',
  $charset       = 'utf8',
  $collate       = 'utf8_general_ci',
  $allowed_hosts = undef
) {

  include ::aodh::deps

  validate_string($password)

  ::openstacklib::db::mysql { 'aodh':
    user          => $user,
    password_hash => mysql_password($password),
    dbname        => $dbname,
    host          => $host,
    charset       => $charset,
    collate       => $collate,
    allowed_hosts => $allowed_hosts,
  }

  Anchor['aodh::db::begin']
  ~> Class['aodh::db::mysql']
  ~> Anchor['aodh::db::end']

}

class aodh::db::mysql 管理了 MySQL aodh 数据库的创建,aodh 用户创建和密码设定,数据库编码,访问授权等。其调用了 openstacklib::db::mysql 来实现上述功能,关于 puppet-openstacklib 模块,本书会在下一章节详细说明。

2.3 class <service>::db::postgresql

class <service>::db::mysql 用于创建相关服务的 PostgreSQL 数据库,用户和授权等。以 Aodh 为例:

class aodh::db::postgresql(
  $password,
  $dbname     = 'aodh',
  $user       = 'aodh',
  $encoding   = undef,
  $privileges = 'ALL',
) {

  include ::aodh::deps

  ::openstacklib::db::postgresql { 'aodh':
    password_hash => postgresql_password($user, $password),
    dbname        => $dbname,
    user          => $user,
    encoding      => $encoding,
    privileges    => $privileges,
  }

  Anchor['aodh::db::begin']
  ~> Class['aodh::db::postgresql']
  ~> Anchor['aodh::db::end']

}

class aodh::db::postgresql 完成了 aodh 数据库的创建,aodh 用户创建和密码设定,数据库编码,访问授权等。其调用了 openstacklib::db::postgresql 来实现上述功能。

2.4 class <service>::db::sync

class aodh::db::sync 用于执行数据库表的初始化和更新操作。以 Aodh 为例:

class aodh::db::sync (
  $user = 'aodh',
){

  include ::aodh::deps

  exec { 'aodh-db-sync':
    command     => 'aodh-dbsync --config-file /etc/aodh/aodh.conf',
    path        => '/usr/bin',
    refreshonly => true,
    user        => $user,
    try_sleep   => 5,
    tries       => 10,
    logoutput   => on_failure,
    subscribe   => [
      Anchor['aodh::install::end'],
      Anchor['aodh::config::end'],
      Anchor['aodh::dbsync::begin']
    ],
    notify      => Anchor['aodh::dbsync::end'],
  }

}

aodh::db::sync 的实现是通过声明 exec 资源来调用 aodh-dbsync 命令行完成数据库初始化的操作。

3. Keystone 初始化管理

在 OpenStack 部署工作中,与 Keystone 相关的初始化操作是集群正常运行必不可少的步骤:

  • 创建 Domain
  • 创建 Project
  • 创建 User,设置 Password
  • 创建并指定 Role
  • 创建 Service
  • 创建 Endpoint

也包括在后期的运维中,指定 user 的 password 更新或者 endpoint 的更改等常见操作都可以在 Puppet 中完成。而这背后的工作是通过 <service>::keystone::auth 来完成的。

3.1 class <service>::keystone::auth

<service>::keystone::auth 用于创建 OpenStack 服务的 user,service 和 endpoint,以 Aodh 为例:

class aodh::keystone::auth (
  $password,
  $auth_name           = 'aodh',
  $email               = 'aodh@localhost',
  $tenant              = 'services',
  $configure_endpoint  = true,
  $configure_user      = true,
  $configure_user_role = true,
  $service_name        = 'aodh',
  $service_type        = 'alarming',
  $region              = 'RegionOne',
  $public_url          = 'http://127.0.0.1:8042',
  $internal_url        = 'http://127.0.0.1:8042',
  $admin_url           = 'http://127.0.0.1:8042',
) {

  include ::aodh::deps

  keystone::resource::service_identity { 'aodh':
    configure_user      => $configure_user,
    configure_user_role => $configure_user_role,
    configure_endpoint  => $configure_endpoint,
    service_name        => $service_name,
    service_type        => $service_type,
    service_description => 'OpenStack Alarming Service',
    region              => $region,
    auth_name           => $auth_name,
    password            => $password,
    email               => $email,
    tenant              => $tenant,
    public_url          => $public_url,
    internal_url        => $internal_url,
    admin_url           => $admin_url,
  }

}

实际上 aodh::keystone::auth 在声明 define keystone::resource::service_identity 的基础上,根据 Aodh 服务而重写了相关的参数。

下面来看一段代码,关于 keystone::resource::service_identity 如何实现 service 的管理:

  if $configure_service {
    if $service_type {
      ensure_resource('keystone_service', "${service_name_real}::${service_type}", {
        'ensure'      => $ensure,
        'description' => $service_description,
      })
    } else {
      fail ('When configuring a service, you need to set the service_type parameter.')
    }
  }

通过函数 ensure_resource 调用了 keystone_service 自定义资源类型,并传入两个参数:

  • "${service_name_real}::${service_type}"
  • {'ensure' => $ensure, 'description' => $service_description,}

有细心的读者读到这里可能会好奇,把服务名称和服务类型作为一个参数传入 keystone_service,它是怎么区分的?

先来看 keystone_service.rb 的代码片段(代码路径 puppet-keystone/lib/puppet/type/keystone_service.rb):

  def self.title_patterns
    PuppetX::Keystone::CompositeNamevar.basic_split_title_patterns(:name, :type)
  end

title_patterns 方法通过调用 PuppetX::Keystone::CompositeNamevar.basic_split_title_patterns 方法来得到 :name:type 变量。

接着跳转到 basic_split_title_patterns 的定义(代码路径 lib/puppet_x/keystone/composite_namevar.rb):

  def self.not_two_colon_regex
    # Anything but 2 consecutive colons.
    Regexp.new(/(?:[^:]|:[^:])+/)
  end

  def self.basic_split_title_patterns(prefix, suffix, separator = '::', *regexps)
    associated_regexps = []
    if regexps.empty? and separator == '::'
      associated_regexps += [not_two_colon_regex, not_two_colon_regex]
    else
      if regexps.count != 2
        raise(Puppet::DevError, 'You must provide two regexps')
      else
        associated_regexps += regexps
      end
    end
    prefix_re = associated_regexps[0]
    suffix_re = associated_regexps[1]
    [
      [
        /^(#{prefix_re})#{separator}(#{suffix_re})$/,
        [
          [prefix],
          [suffix]
        ]
      ],
      [
        /^(#{prefix_re})$/,
        [
          [prefix]
        ]
      ]
    ]
  end

可以看到 basic_split_title_patterns 方法默认使用'::'作为分隔符,通过 not_two_colon_regex 函数进行正则匹配并切割字符串。 至此,我们从上到下地剖析了如何实现 Keystone 相关资源的初始化,以加深读者对于代码的理解。在实际使用中,对于终端用户来说,并不需要关心底层的 Ruby 代码。

3.2 class <service>::keystone::authtoken

<service>::keystone::authtoken 用于管理 OpenStack 各服务配置文件中的 keystone_authtoken 配置节。以 Aodh 服务为例:

class  aodh::keystone::authtoken(
...){

  ...

  keystone::resource::authtoken { 'aodh_config':
    username                       => $username,
    password                       => $password,
    project_name                   => $project_name,
    auth_url                       => $auth_url,
    auth_uri                       => $auth_uri,
    auth_version                   => $auth_version,
    auth_type                      => $auth_type,
    auth_section                   => $auth_section,
    ...
    memcache_pool_conn_get_timeout => $memcache_pool_conn_get_timeout,
    memcache_pool_dead_retry       => $memcache_pool_dead_retry,
    memcache_pool_maxsize          => $memcache_pool_maxsize,
    memcache_pool_socket_timeout   => $memcache_pool_socket_timeout,
    ...
  }
}

aodh::keystone::authtoken 定义中声明了 define keystone::resource::authtoken ,并重写了部分参数的默认值。 keystone::resource::authtoken 中定义了 hash 类型变量$keystonemiddleware_options,涵盖了 keystone_authtoken 配置节下的所有参数, 最终通过调用 create_resources 函数,传入服务名称参数$name,从而完成指定服务配置文件中 keystone_authtoken 的配置。

  $keystonemiddleware_options = {
    'keystone_authtoken/auth_section'                   => {'value' => $auth_section},
    'keystone_authtoken/auth_uri'                       => {'value' => $auth_uri},
    'keystone_authtoken/auth_type'                      => {'value' => $auth_type},
    'keystone_authtoken/auth_version'                   => {'value' => $auth_version},
    'keystone_authtoken/cache'                          => {'value' => $cache},
     ...
    'keystone_authtoken/username'                       => {'value' => $username},
    'keystone_authtoken/password'                       => {'value' => $password, 'secret' => true},
    'keystone_authtoken/user_domain_name'               => {'value' => $user_domain_name},
    'keystone_authtoken/project_name'                   => {'value' => $project_name},
    'keystone_authtoken/project_domain_name'            => {'value' => $project_domain_name},
    'keystone_authtoken/insecure'                       => {'value' => $insecure},
  }
  create_resources($name, $keystonemiddleware_options)

4.维护不同 Linux 发行版之间的数据

PuppetOpenstack 支持在 Redhat, CentOS, Ubuntu 等多个 Linux 发行版上部署 OpenStack 服务,然而在不同的 Linux 发行版中,同一个 OpenStack 服务的软件包的名称会有所不同。

例如,Nova API 软件包的名称在 Redhat 下是'openstack-nova-api',在 Debian 下是'nova-api'。

而这些数据则通过各个模块的 class <service>::params 维护。

以 keystone::params 为例,可以看到不同的 Linux 发行版之间$package_name, $service_name 等参数值也有所不同:

class  keystone::params {
  include ::openstacklib::defaults
  $client_package_name = 'python-keystoneclient'
  $keystone_user       = 'keystone'
  $keystone_group      = 'keystone'
  $keystone_wsgi_admin_script_path  = '/usr/bin/keystone-wsgi-admin'
  $keystone_wsgi_public_script_path = '/usr/bin/keystone-wsgi-public'
  case $::osfamily {
    'Debian': {
      $package_name                 = 'keystone'
      $service_name                 = 'keystone'
      $keystone_wsgi_script_path    = '/usr/lib/cgi-bin/keystone'
      $python_memcache_package_name = 'python-memcache'
      $mellon_package_name          = 'libapache2-mod-auth-mellon'
      $openidc_package_name         = 'libapache2-mod-auth-openidc'
    }
    'RedHat': {
      $package_name                 = 'openstack-keystone'
      $service_name                 = 'openstack-keystone'
      $keystone_wsgi_script_path    = '/var/www/cgi-bin/keystone'
      $python_memcache_package_name = 'python-memcached'
      $mellon_package_name          = 'mod_auth_mellon'
      $openidc_package_name         = 'mod_auth_openidc'
    }
    default: {
      fail("Unsupported osfamily ${::osfamily}")
    }
  }
}

5. 管理自定义配置项的 <service>::config

模板是用于管理配置文件的常见方式,对于成熟的项目而言,模板是一种理想的管理配置文件方式。但对于快速迭代的项目如 OpenStack,维护人员会非常痛苦,每增删一个配置项需要同时更新模板和 manifets 文件。

试想一个 module 的更新若都在参数的增添上,那对社区开发者来说是极大的成本。有没有一种办法可以不修改 module,直接在 hiera 里定义来添加新配置项呢?

<service>::config 类是由笔者在 14 年初提出的特性,目的是灵活地管理自定义配置项。

自定义配置项是指未被模块管理的参数。怎么理解?

keystone::config 为例,其核心是 create_resources 函数以及 keystone_config/keystone_paste_init 自定义资源:

# == Class: keystone::config
#
# This class is used to manage arbitrary keystone configurations.
#
# === Parameters
#
# [*keystone_config*]
#   (optional) Allow configuration of arbitrary keystone configurations.
#   The value is an hash of keystone_config resources. Example:
#   { 'DEFAULT/foo' => { value => 'fooValue'},
#     'DEFAULT/bar' => { value => 'barValue'}
#   }
#   In yaml format, Example:
#   keystone_config:
#     DEFAULT/foo:
#       value: fooValue
#     DEFAULT/bar:
#       value: barValue
#
# [*keystone_paste_ini*]
#   (optional) Allow configuration of /etc/keystone/keystone-paste.ini options.
#
#   NOTE: The configuration MUST NOT be already handled by this module
#   or Puppet catalog compilation will fail with duplicate resources.
#
class keystone::config (
  $keystone_config  = {},
  $keystone_paste_ini = {},
) {

  include ::keystone::deps

  validate_hash($keystone_config)
  validate_hash($keystone_paste_ini)

  create_resources('keystone_config', $keystone_config)
  create_resources('keystone_paste_ini', $keystone_paste_ini)
}

若 Keystone 在某版本新增了参数 new_param,在 puppet-keystone 模块里没有该参数,此时,只要使用 keystone::config 就可以轻松完成参数的管理。

在 hiera 文件中添加以下代码:

---
   keystone::config::keystone_config:
     DEFAULT/new_param:
       value: newValue

6.管理客户端 <service>::client

<service>::client 用于管理各 OpenStack 服务的 Client 端,完成客户端的安装。

以 Nova 为例,nova::client 完成了 python-novaclient 软件包的安装:

class nova::client(
  $ensure = 'present'
) {
  include ::nova::deps

  package { 'python-novaclient':
    ensure => $ensure,
    tag    => ['openstack', 'nova-support-package'],
  }

}

7. 管理策略 <service>::policy

<service>::policy 用于管理 Openstack 各服务的策略文件 policy.json。

以 Cinder 为例,下面是 cinder::policy 代码:

class cinder::policy (
  $policies    = {},
  $policy_path = '/etc/cinder/policy.json',
) {

  include ::cinder::deps

  validate_hash($policies)

  Openstacklib::Policy::Base {
    file_path => $policy_path,
  }

  create_resources('openstacklib::policy::base', $policies)
  oslo::policy { 'cinder_config': policy_file => $policy_path }

}

其中使用 create_resources 调用了 openstacklib::policy::base ,以及声明了 oslo::policy 定义。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文