File: aix.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (169 lines) | stat: -rw-r--r-- 5,220 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
require_relative '../../../puppet/provider/package'
require_relative '../../../puppet/util/package'

Puppet::Type.type(:package).provide :aix, :parent => Puppet::Provider::Package do
  desc "Installation from an AIX software directory, using the AIX `installp`
       command.  The `source` parameter is required for this provider, and should
       be set to the absolute path (on the puppet agent machine) of a directory
       containing one or more BFF package files.

       The `installp` command will generate a table of contents file (named `.toc`)
       in this directory, and the `name` parameter (or resource title) that you
       specify for your `package` resource must match a package name that exists
       in the `.toc` file.

       Note that package downgrades are *not* supported; if your resource specifies
       a specific version number and there is already a newer version of the package
       installed on the machine, the resource will fail with an error message."

  # The commands we are using on an AIX box are installed standard
  # (except nimclient) nimclient needs the bos.sysmgt.nim.client fileset.
  commands    :lslpp => "/usr/bin/lslpp",
              :installp => "/usr/sbin/installp"

  # AIX supports versionable packages with and without a NIM server
  has_feature :versionable

  confine  :operatingsystem => [ :aix ]
  defaultfor :operatingsystem => :aix

  attr_accessor   :latest_info

  STATE_CODE = {
    'A' => :applied,
    'B' => :broken,
    'C' => :committed,
    'E' => :efix_locked,
    'O' => :obsolete,
    '?' => :inconsistent,
  }.freeze

  def self.srclistcmd(source)
    [ command(:installp), "-L", "-d", source ]
  end

  def self.prefetch(packages)
    raise Puppet::Error, _("The aix provider can only be used by root") if Process.euid != 0

    return unless packages.detect { |name, package| package.should(:ensure) == :latest }

    sources = packages.collect { |name, package| package[:source] }.uniq.compact

    updates = {}
    sources.each do |source|
      execute(self.srclistcmd(source)).each_line do |line|
        if line =~ /^[^#][^:]*:([^:]*):([^:]*)/
          current = {}
          current[:name]    = $1
          current[:version] = $2
          current[:source]  = source

          if updates.key?(current[:name])
            previous = updates[current[:name]]

            updates[current[:name]] = current unless Puppet::Util::Package.versioncmp(previous[:version], current[:version]) == 1

          else
            updates[current[:name]] = current
          end
        end
      end
    end

    packages.each do |name, package|
      if updates.key?(name)
        package.provider.latest_info = updates[name]
      end
    end
  end

  def uninstall
    # Automatically process dependencies when installing/uninstalling
    # with the -g option to installp.
    installp "-gu", @resource[:name]

    # installp will return an exit code of zero even if it didn't uninstall
    # anything... so let's make sure it worked.
    unless query().nil?
      self.fail _("Failed to uninstall package '%{name}'") % { name: @resource[:name] }
    end
  end

  def install(useversion = true)
    source = @resource[:source]
    unless source
      self.fail _("A directory is required which will be used to find packages")
    end

    pkg = @resource[:name]

    pkg += " #{@resource.should(:ensure)}" if (! @resource.should(:ensure).is_a? Symbol) and useversion

    output = installp "-acgwXY", "-d", source, pkg

    # If the package is superseded, it means we're trying to downgrade and we
    # can't do that.
    if output =~ /^#{Regexp.escape(@resource[:name])}\s+.*\s+Already superseded by.*$/
      self.fail _("aix package provider is unable to downgrade packages")
    end

    pkg_info = query
    if pkg_info && [:broken, :inconsistent].include?(pkg_info[:status])
      self.fail _("Package '%{name}' is in a %{status} state and requires manual intervention") % { name: @resource[:name], status: pkg_info[:status] }
    end
  end

  def self.pkglist(hash = {})
    cmd = [command(:lslpp), "-qLc"]

    name = hash[:pkgname]
    if name
      cmd << name
    end

    begin
      list = execute(cmd).scan(/^[^#][^:]*:([^:]*):([^:]*):[^:]*:[^:]*:([^:])/).collect { |n,e,s|
        e = :absent if [:broken, :inconsistent].include?(STATE_CODE[s])
        { :name => n, :ensure => e, :status => STATE_CODE[s], :provider => self.name }
      }
    rescue Puppet::ExecutionFailure => detail
      if hash[:pkgname]
        return nil
      else
        raise Puppet::Error, _("Could not list installed Packages: %{detail}") % { detail: detail }, detail.backtrace
      end
    end

    if hash[:pkgname]
      return list.shift
    else
      return list
    end
  end

  def self.instances
    pkglist.collect do |hash|
      new(hash)
    end
  end

  def latest
    upd = latest_info

    unless upd.nil?
      return "#{upd[:version]}"
    else
      raise Puppet::DevError, _("Tried to get latest on a missing package") if properties[:ensure] == :absent

      return properties[:ensure]
    end
  end

  def query
    self.class.pkglist(:pkgname => @resource[:name])
  end

  def update
    self.install(false)
  end
end