summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Client/Tools/Portage.py
blob: 4516f419d83bafc5c87bfdb61e3aba2640154598 (plain)
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
"""This is the Bcfg2 tool for the Gentoo Portage system."""

import re
import Bcfg2.Client.Tools
from Bcfg2.Bcfg2Py3k import ConfigParser


class Portage(Bcfg2.Client.Tools.PkgTool):
    """The Gentoo toolset implements package and service operations and
    inherits the rest from Toolset.Toolset."""
    name = 'Portage'
    __execs__ = ['/usr/bin/emerge', '/usr/bin/equery']
    __handles__ = [('Package', 'ebuild')]
    __req__ = {'Package': ['name', 'version']}
    pkgtype = 'ebuild'
    # requires a working PORTAGE_BINHOST in make.conf
    _binpkgtool = ('emerge --getbinpkgonly %s', ('=%s-%s', \
                                     ['name', 'version']))
    pkgtool = ('emerge %s', ('=%s-%s', ['name', 'version']))

    def __init__(self, logger, cfg, setup):
        self._initialised = False
        Bcfg2.Client.Tools.PkgTool.__init__(self, logger, cfg, setup)
        self._initialised = True
        self.__important__ = self.__important__ + ['/etc/make.conf']
        self._pkg_pattern = re.compile('(.*)-(\d.*)')
        self._ebuild_pattern = re.compile('(ebuild|binary)')
        self.cfg = cfg
        self.installed = {}
        self._binpkgonly = True

        # Used to get options from configuration file
        parser = ConfigParser.ConfigParser()
        parser.read(self.setup.get('setup'))
        for opt in ['binpkgonly']:
            if parser.has_option(self.name, opt):
                setattr(self, ('_%s' % opt),
                        self._StrToBoolIfBool(parser.get(self.name, opt)))

        if self._binpkgonly:
            self.pkgtool = self._binpkgtool
        self.RefreshPackages()

    def _StrToBoolIfBool(self, s):
        """Returns a boolean if the string specifies a boolean value.
           Returns a string otherwise"""
        if s.lower() in ('true', 'yes', 't', 'y', '1'):
            return True
        elif s.lower() in ('false', 'no', 'f', 'n', '0'):
            return False
        else:
            return s

    def RefreshPackages(self):
        """Refresh memory hashes of packages."""
        if not self._initialised:
            return
        self.logger.info('Getting list of installed packages')
        cache = self.cmd.run("equery -q list '*'")[1]
        self.installed = {}
        for pkg in cache:
            if self._pkg_pattern.match(pkg):
                name = self._pkg_pattern.match(pkg).group(1)
                version = self._pkg_pattern.match(pkg).group(2)
                self.installed[name] = version
            else:
                self.logger.info("Failed to parse pkg name %s" % pkg)

    def VerifyPackage(self, entry, modlist):
        """Verify package for entry."""
        if not 'version' in entry.attrib:
            self.logger.info("Cannot verify unversioned package %s" %
                             (entry.get('name')))
            return False

        if not (entry.get('name') in self.installed):
            # Can't verify package that isn't installed
            entry.set('current_exists', 'false')
            return False

        # get the installed version
        version = self.installed[entry.get('name')]
        entry.set('current_version', version)

        if not self.setup['quick']:
            if ('verify' not in entry.attrib) or \
                self._StrToBoolIfBool(entry.get('verify')):

            # Check the package if:
            # - Not running in quick mode
            # - No verify option is specified in the literal configuration
            #    OR
            # - Verify option is specified and is true

                self.logger.debug('Running equery check on %s' %
                                  entry.get('name'))
                output = self.cmd.run("/usr/bin/equery -N check '=%s-%s' "
                                      "2>&1 | grep '!!!' | awk '{print $2}'"
                                      % ((entry.get('name'), version)))[1]
                if [filename for filename in output \
                    if filename not in modlist]:
                    return False

        # By now the package must be in one of the following states:
        # - Not require checking
        # - Have no files modified at all
        # - Have modified files in the modlist only
        if self.installed[entry.get('name')] == version:
            # Specified package version is installed
            # Specified package version may be any in literal configuration
            return True

        # Something got skipped. Indicates a bug
        return False

    def RemovePackages(self, packages):
        """Deal with extra configuration detected."""
        pkgnames = " ".join([pkg.get('name') for pkg in packages])
        if len(packages) > 0:
            self.logger.info('Removing packages:')
            self.logger.info(pkgnames)
            self.cmd.run("emerge --unmerge --quiet %s" %
                         " ".join(pkgnames.split(' ')))
            self.RefreshPackages()
            self.extra = self.FindExtraPackages()