当前位置:  开发笔记 > 编程语言 > 正文

puppet如何向操作系统发送命令?

如何解决《puppet如何向操作系统发送命令?》经验,为你挑选了1个好方法。

我是Puppet的新手,但很清楚这些概念.Puppet Manifests调用Puppet Modules,模块执行实际任务.

我试图了解Puppet模块层发生了什么.命令如何实际执行?以下面的例子为例,哪些命令实际传递给操作系统?此外,定义在哪里?

package { 'ntp':
  ensure => installed,
}

Peter Souter.. 10

简介: Puppet根据facts系统和Puppet本身的配置确定需要运行的命令.

因此,当Puppet编译目录以在其系统上运行时,它看起来如下所示:

"我需要安装一个名为ntp的Pacakge资源.我是RedHat系列的CentOS系统.默认情况下,在RedHat上,我使用yum命令.所以我需要运行yum install ntp"

更长的解释

Puppet知道运行命令以及如何运行它们的方式称为资源抽象层.

当它全部归结时,Puppet没有做任何神奇的事情:在系统上运行的命令与人类操作员运行的命令相同.

也许Puppet已经想出了一个聪明的方法来做到这一点,并考虑到你所使用的平台上的模糊错误和陷阱,或者是因为你想要做的事情包含拼写错误或类似错误而引发错误.

但最终,必须使用系统实际应用程序和工具来执行操作.

这就是RAL实际进入的地方.它是Puppet中最大的抽象层:将与基本系统的所有交互转换为一致的界面.

在您给出的示例中,包非常简单.至少在过去二十年中,安装软件包的概念(大多数)几乎与每个操作系统相同:

packagesystemtool keywordforinstall packagename

通常,install关键字是install,但也有一些例外.BSD的pkg,它使用pkg add的例子.

但是:可以在该包中管理的实际属性可能有很大差异:

你能指定版本吗?

你能降级那个版本吗?

如果已安装软件包,是否需要指定其他命令进行升级?

一大堆其他可选参数,例如代理信息,错误记录级别.

RAL允许用户以一致的方式定义资源的特征,而不管实现如何:

type { 'title':  
  attribute => 'value',
}

每个资源都遵循相同的语法:

资源类型(例如,用户,包,服务,文件)

用于定义资源块的大括号.

标题,与资源主体分隔,带有冒号,包含属性和值对

所以我们的包声明如下:

package {'tree':  
  ensure => 'present',
}

RAL可以在已定义的每个平台上处理该行为,并且在可用的情况下支持不同的包功能,所有这些功能都以明确定义的方式,默认情况下对用户隐藏.

我听过RAL最好的比喻是天鹅在湖上滑行:

当你在水体上看天鹅时,它看起来优雅而优雅,滑翔.它看起来几乎没有工作.

眼睛隐藏的是水面下的活动.那只天鹅正在踢它的蹼足,不那么优雅,它看起来很顶:Puppet正在运行的实际命令是在水下踢腿.

好的,有足够的背景,你可能会问......

它是如何实际工作的? RAL将系统上的所有资源拆分为两个元素:

类型:资源的有效属性的高级模型

提供者:类型的特定于平台的实现

这使您可以以适用于任何系统的方式描述资源.每种资源,无论它是什么,都有一个或多个提供者.提供程序是底层操作系统和资源类型之间的接口.

通常,类型的默认提供程序,但您可以根据需要指定特定的提供程序.

对于包,默认提供者将是系统的默认包提供者:yum对于RHEL,apt对于Debian,pkg对于BSD等.这由a确定,其从系统获取事实.

例如,yum提供者具有以下内容:

defaultfor :osfamily => :redhat

但是你可能想要安装一个pip包或一个gem.为此,您将指定提供程序,因此它将使用不同的命令安装它:

package {'tree':  
  ensure   => 'present',
  provider => 'pip', 
}

这意味着我们对RAL说:"嘿,我知道yum是默认安装包,但这是我需要的python包,所以我告诉你使用pip代替"

无论实际实现如何不同,属性类型的最重要资源在操作系统中通常在概念上是相同的.

就像我们说的那样,大多数软件包都会安装 package installer install packagename

因此,资源的描述可以从其实现中抽象出来:

Puppet使用RAL来读取和修改系统上的资源状态.由于它是一个声明性系统,Puppet首先要了解资源应该具有什么状态.

要同步资源,它使用RAL查询当前状态,将其与所需状态进行比较,然后再次使用RAL进行任何必要的更改.它使用工具来获取系统的当前状态,然后确定将状态更改为资源定义的状态需要执行的操作.

当Puppet应用包含资源的目录时,它将读取目标系统上资源的实际状态,将实际状态与所需状态进行比较,并在必要时更改系统以强制执行所需状态.

让我们来看看RAL将如何管理:

我们已经将类型作为包.

包的标题/名称是 ntp

我在RHEL7系统上运行它,因此默认提供程序是yum.

yum是rpm的"子"提供程序:它使用RPM命令检查程序包是否已安装在系统上.(这比运行"yum info"快得多,因为它不会进行任何互联网呼叫,并且如果yumrepo失败则不会失败)

然而,安装命令将是 yum install

所以之前我们讨论过Puppet如何使用RAL来读取和修改系统上的资源状态.

RAL的"getter"是提供者中的self.instances方法.

根据资源类型,通常以两种方式之一完成:

读取磁盘上的文件,遍历文件中的行并将其转换为资源

在终端上运行命令,将stdout分解为行,将其转换为成为资源的哈希值

rpm实例步骤与后者相同.它运行rpm -qa一些给定的标志来检查系统上的软件包:

def self.instances  
    packages = []

    # list out all of the packages
    begin
      execpipe("#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf '#{self::NEVRA_FORMAT}'") { |process|
        # now turn each returned line into a package object
        process.each_line { |line|
          hash = nevra_to_hash(line)
          packages << new(hash) unless hash.empty?
        }
      }
    rescue Puppet::ExecutionFailure
      raise Puppet::Error, "Failed to list packages", $!.backtrace
    end

    packages
  end

所以它正在运行/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n',然后从该命令获取stdout,循环遍历每行输出,并使用该nevra_to_hash方法将STDOUT行转换为哈希.

self::NEVRA_REGEX  = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$}  
self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]

private  
  # @param line [String] one line of rpm package query information
  # @return [Hash] of NEVRA_FIELDS strings parsed from package info
  # or an empty hash if we failed to parse
  # @api private
  def self.nevra_to_hash(line)
    line.strip!
    hash = {}

    if match = self::NEVRA_REGEX.match(line)
      self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v }
      hash[:provider] = self.name
      hash[:ensure] = "#{hash[:version]}-#{hash[:release]}"
      hash[:ensure].prepend("#{hash[:epoch]}:") if hash[:epoch] != '0'
    else
      Puppet.debug("Failed to match rpm line #{line}")
    end

    return hash
  end

所以基本上它是输出的正则表达式,然后将这些位从正则表达式转换为给定的字段.

这些哈希成为资源的当前状态.

我们可以运行--debug来看到这个:

Debug: Prefetching yum resources for package  
Debug: Executing: '/usr/bin/rpm --version'  
Debug: Executing '/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n''  
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n'  
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n --whatprovides'  

所以它使用RAL来获取当前状态.Puppet正在做以下事情:

嗯,这是一个RHEL系统上名为'ntp'的包资源,所以我应该使用RPM

让我们了解安装的RPM包的当前状态(例如,实例方法)

ntp 不在这里......

所以我们需要ntp安装

然后,Yum提供程序指定安装所需的命令.

这里有很多逻辑:

def install  
    wanted = @resource[:name]
    error_level = self.class.error_level
    update_command = self.class.update_command
    # If not allowing virtual packages, do a query to ensure a real package exists
    unless @resource.allow_virtual?
      execute([command(:cmd), '-d', '0', '-e', error_level, '-y', install_options, :list, wanted].compact)
    end

    should = @resource.should(:ensure)
    self.debug "Ensuring => #{should}"
    operation = :install

    case should
    when :latest
      current_package = self.query
      if current_package && !current_package[:ensure].to_s.empty?
        operation = update_command
        self.debug "Ensuring latest, so using #{operation}"
      else
        self.debug "Ensuring latest, but package is absent, so using #{:install}"
        operation = :install
      end
      should = nil
    when true, false, Symbol
      # pass
      should = nil
    else
      # Add the package version
      wanted += "-#{should}"
      if wanted.scan(ARCH_REGEX)
        self.debug "Detected Arch argument in package! - Moving arch to end of version string"
        wanted.gsub!(/(.+)(#{ARCH_REGEX})(.+)/,'\1\3\2')
      end

      current_package = self.query
      if current_package
        if rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) < 0
          self.debug "Downgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
          operation = :downgrade
        elsif rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) > 0
          self.debug "Upgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
          operation = update_command
        end
      end
    end

    # Yum on el-4 and el-5 returns exit status 0 when trying to install a package it doesn't recognize;
    # ensure we capture output to check for errors.
    no_debug = if Facter.value(:operatingsystemmajrelease).to_i > 5 then ["-d", "0"] else [] end
    command = [command(:cmd)] + no_debug + ["-e", error_level, "-y", install_options, operation, wanted].compact
    output = execute(command)

    if output =~ /^No package #{wanted} available\.$/
      raise Puppet::Error, "Could not find package #{wanted}"
    end

    # If a version was specified, query again to see if it is a matching version
    if should
      is = self.query
      raise Puppet::Error, "Could not find package #{self.name}" unless is

      # FIXME: Should we raise an exception even if should == :latest
      # and yum updated us to a version other than @param_hash[:ensure] ?
      vercmp_result = rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is[:ensure]))
      raise Puppet::Error, "Failed to update to version #{should}, got version #{is[:ensure]} instead" if vercmp_result != 0
    end
  end

这是一些严重的天鹅腿踢.这里有很多逻辑,对于Yum上包的更复杂的用例,但确保它适用于各种版本的Yum avaliable,包括RHEL 4和5.

逻辑被分解:我们没有指定版本,所以我们不需要检查要安装的版本.只需使用指定的默认选项运行yum install tr​​ee

Debug: Package[tree](provider=yum): Ensuring => present  
Debug: Executing: '/usr/bin/yum -d 0 -e 0 -y install tree'  
Notice: /Stage[main]/Main/Package[tree]/ensure: created  

Ta-dah,已安装.



1> Peter Souter..:

简介: Puppet根据facts系统和Puppet本身的配置确定需要运行的命令.

因此,当Puppet编译目录以在其系统上运行时,它看起来如下所示:

"我需要安装一个名为ntp的Pacakge资源.我是RedHat系列的CentOS系统.默认情况下,在RedHat上,我使用yum命令.所以我需要运行yum install ntp"

更长的解释

Puppet知道运行命令以及如何运行它们的方式称为资源抽象层.

当它全部归结时,Puppet没有做任何神奇的事情:在系统上运行的命令与人类操作员运行的命令相同.

也许Puppet已经想出了一个聪明的方法来做到这一点,并考虑到你所使用的平台上的模糊错误和陷阱,或者是因为你想要做的事情包含拼写错误或类似错误而引发错误.

但最终,必须使用系统实际应用程序和工具来执行操作.

这就是RAL实际进入的地方.它是Puppet中最大的抽象层:将与基本系统的所有交互转换为一致的界面.

在您给出的示例中,包非常简单.至少在过去二十年中,安装软件包的概念(大多数)几乎与每个操作系统相同:

packagesystemtool keywordforinstall packagename

通常,install关键字是install,但也有一些例外.BSD的pkg,它使用pkg add的例子.

但是:可以在该包中管理的实际属性可能有很大差异:

你能指定版本吗?

你能降级那个版本吗?

如果已安装软件包,是否需要指定其他命令进行升级?

一大堆其他可选参数,例如代理信息,错误记录级别.

RAL允许用户以一致的方式定义资源的特征,而不管实现如何:

type { 'title':  
  attribute => 'value',
}

每个资源都遵循相同的语法:

资源类型(例如,用户,包,服务,文件)

用于定义资源块的大括号.

标题,与资源主体分隔,带有冒号,包含属性和值对

所以我们的包声明如下:

package {'tree':  
  ensure => 'present',
}

RAL可以在已定义的每个平台上处理该行为,并且在可用的情况下支持不同的包功能,所有这些功能都以明确定义的方式,默认情况下对用户隐藏.

我听过RAL最好的比喻是天鹅在湖上滑行:

当你在水体上看天鹅时,它看起来优雅而优雅,滑翔.它看起来几乎没有工作.

眼睛隐藏的是水面下的活动.那只天鹅正在踢它的蹼足,不那么优雅,它看起来很顶:Puppet正在运行的实际命令是在水下踢腿.

好的,有足够的背景,你可能会问......

它是如何实际工作的? RAL将系统上的所有资源拆分为两个元素:

类型:资源的有效属性的高级模型

提供者:类型的特定于平台的实现

这使您可以以适用于任何系统的方式描述资源.每种资源,无论它是什么,都有一个或多个提供者.提供程序是底层操作系统和资源类型之间的接口.

通常,类型的默认提供程序,但您可以根据需要指定特定的提供程序.

对于包,默认提供者将是系统的默认包提供者:yum对于RHEL,apt对于Debian,pkg对于BSD等.这由a确定,其从系统获取事实.

例如,yum提供者具有以下内容:

defaultfor :osfamily => :redhat

但是你可能想要安装一个pip包或一个gem.为此,您将指定提供程序,因此它将使用不同的命令安装它:

package {'tree':  
  ensure   => 'present',
  provider => 'pip', 
}

这意味着我们对RAL说:"嘿,我知道yum是默认安装包,但这是我需要的python包,所以我告诉你使用pip代替"

无论实际实现如何不同,属性类型的最重要资源在操作系统中通常在概念上是相同的.

就像我们说的那样,大多数软件包都会安装 package installer install packagename

因此,资源的描述可以从其实现中抽象出来:

Puppet使用RAL来读取和修改系统上的资源状态.由于它是一个声明性系统,Puppet首先要了解资源应该具有什么状态.

要同步资源,它使用RAL查询当前状态,将其与所需状态进行比较,然后再次使用RAL进行任何必要的更改.它使用工具来获取系统的当前状态,然后确定将状态更改为资源定义的状态需要执行的操作.

当Puppet应用包含资源的目录时,它将读取目标系统上资源的实际状态,将实际状态与所需状态进行比较,并在必要时更改系统以强制执行所需状态.

让我们来看看RAL将如何管理:

我们已经将类型作为包.

包的标题/名称是 ntp

我在RHEL7系统上运行它,因此默认提供程序是yum.

yum是rpm的"子"提供程序:它使用RPM命令检查程序包是否已安装在系统上.(这比运行"yum info"快得多,因为它不会进行任何互联网呼叫,并且如果yumrepo失败则不会失败)

然而,安装命令将是 yum install

所以之前我们讨论过Puppet如何使用RAL来读取和修改系统上的资源状态.

RAL的"getter"是提供者中的self.instances方法.

根据资源类型,通常以两种方式之一完成:

读取磁盘上的文件,遍历文件中的行并将其转换为资源

在终端上运行命令,将stdout分解为行,将其转换为成为资源的哈希值

rpm实例步骤与后者相同.它运行rpm -qa一些给定的标志来检查系统上的软件包:

def self.instances  
    packages = []

    # list out all of the packages
    begin
      execpipe("#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf '#{self::NEVRA_FORMAT}'") { |process|
        # now turn each returned line into a package object
        process.each_line { |line|
          hash = nevra_to_hash(line)
          packages << new(hash) unless hash.empty?
        }
      }
    rescue Puppet::ExecutionFailure
      raise Puppet::Error, "Failed to list packages", $!.backtrace
    end

    packages
  end

所以它正在运行/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n',然后从该命令获取stdout,循环遍历每行输出,并使用该nevra_to_hash方法将STDOUT行转换为哈希.

self::NEVRA_REGEX  = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$}  
self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]

private  
  # @param line [String] one line of rpm package query information
  # @return [Hash] of NEVRA_FIELDS strings parsed from package info
  # or an empty hash if we failed to parse
  # @api private
  def self.nevra_to_hash(line)
    line.strip!
    hash = {}

    if match = self::NEVRA_REGEX.match(line)
      self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v }
      hash[:provider] = self.name
      hash[:ensure] = "#{hash[:version]}-#{hash[:release]}"
      hash[:ensure].prepend("#{hash[:epoch]}:") if hash[:epoch] != '0'
    else
      Puppet.debug("Failed to match rpm line #{line}")
    end

    return hash
  end

所以基本上它是输出的正则表达式,然后将这些位从正则表达式转换为给定的字段.

这些哈希成为资源的当前状态.

我们可以运行--debug来看到这个:

Debug: Prefetching yum resources for package  
Debug: Executing: '/usr/bin/rpm --version'  
Debug: Executing '/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n''  
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n'  
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n --whatprovides'  

所以它使用RAL来获取当前状态.Puppet正在做以下事情:

嗯,这是一个RHEL系统上名为'ntp'的包资源,所以我应该使用RPM

让我们了解安装的RPM包的当前状态(例如,实例方法)

ntp 不在这里......

所以我们需要ntp安装

然后,Yum提供程序指定安装所需的命令.

这里有很多逻辑:

def install  
    wanted = @resource[:name]
    error_level = self.class.error_level
    update_command = self.class.update_command
    # If not allowing virtual packages, do a query to ensure a real package exists
    unless @resource.allow_virtual?
      execute([command(:cmd), '-d', '0', '-e', error_level, '-y', install_options, :list, wanted].compact)
    end

    should = @resource.should(:ensure)
    self.debug "Ensuring => #{should}"
    operation = :install

    case should
    when :latest
      current_package = self.query
      if current_package && !current_package[:ensure].to_s.empty?
        operation = update_command
        self.debug "Ensuring latest, so using #{operation}"
      else
        self.debug "Ensuring latest, but package is absent, so using #{:install}"
        operation = :install
      end
      should = nil
    when true, false, Symbol
      # pass
      should = nil
    else
      # Add the package version
      wanted += "-#{should}"
      if wanted.scan(ARCH_REGEX)
        self.debug "Detected Arch argument in package! - Moving arch to end of version string"
        wanted.gsub!(/(.+)(#{ARCH_REGEX})(.+)/,'\1\3\2')
      end

      current_package = self.query
      if current_package
        if rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) < 0
          self.debug "Downgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
          operation = :downgrade
        elsif rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) > 0
          self.debug "Upgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
          operation = update_command
        end
      end
    end

    # Yum on el-4 and el-5 returns exit status 0 when trying to install a package it doesn't recognize;
    # ensure we capture output to check for errors.
    no_debug = if Facter.value(:operatingsystemmajrelease).to_i > 5 then ["-d", "0"] else [] end
    command = [command(:cmd)] + no_debug + ["-e", error_level, "-y", install_options, operation, wanted].compact
    output = execute(command)

    if output =~ /^No package #{wanted} available\.$/
      raise Puppet::Error, "Could not find package #{wanted}"
    end

    # If a version was specified, query again to see if it is a matching version
    if should
      is = self.query
      raise Puppet::Error, "Could not find package #{self.name}" unless is

      # FIXME: Should we raise an exception even if should == :latest
      # and yum updated us to a version other than @param_hash[:ensure] ?
      vercmp_result = rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is[:ensure]))
      raise Puppet::Error, "Failed to update to version #{should}, got version #{is[:ensure]} instead" if vercmp_result != 0
    end
  end

这是一些严重的天鹅腿踢.这里有很多逻辑,对于Yum上包的更复杂的用例,但确保它适用于各种版本的Yum avaliable,包括RHEL 4和5.

逻辑被分解:我们没有指定版本,所以我们不需要检查要安装的版本.只需使用指定的默认选项运行yum install tr​​ee

Debug: Package[tree](provider=yum): Ensuring => present  
Debug: Executing: '/usr/bin/yum -d 0 -e 0 -y install tree'  
Notice: /Stage[main]/Main/Package[tree]/ensure: created  

Ta-dah,已安装.

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有