summaryrefslogtreecommitdiffstats
path: root/src/software/test/package.py
blob: 500f8b5fac04878bb2d3aea2b9f602bb33f16467 (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
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/python
# -*- Coding:utf-8 -*-
#
# Copyright (C) 2012-2013 Red Hat, Inc.  All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details. #
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#
# Authors: Michal Minar <miminar@redhat.com>
"""
Abstraction for RPM package for test purposes.
"""

import subprocess

import util

class Package(object):
    """
    Element of test package database. It's a container for package
    informations.
    """
    def __init__(self, name, epoch, ver, rel, arch, repoid,
            rpm_path, files=None):
        self._name = name
        if not epoch or (isinstance(epoch, basestring)
                and epoch.lower() == "(none)"):
            epoch = "0"
        self._epoch = int(epoch)
        self._ver = ver
        self._rel = rel
        self._arch = arch
        self._repoid = repoid
        self._rpm_path = rpm_path
        if files is None:
            files = set()
        else:
            files = set(files)
        self._files = files

    def __str__(self):
        return self.get_nevra()

    @property
    def name(self):
        """ :returns: Package name. """
        return self._name
    @property
    def epoch(self):
        """ :returns: Package epoch as an integer. """
        return self._epoch
    @property
    def ver(self):
        """ :returns: Version string of package. """
        return self._ver
    @property
    def rel(self):
        """ :returns: Release string of package. """
        return self._rel
    @property
    def arch(self):
        """ :returns: Architecture string of package. """
        return self._arch
    @property
    def repoid(self):
        """ :returns: Repository id of package, where it is available. """
        return self._repoid
    @property
    def nevra(self):
        """
        :returns: Nevra string of package with epoch part always present.
        """
        return self.get_nevra('ALWAYS')
    @property
    def evra(self):
        """
        :returns: Evra string of package. That's the same as *nevra* without a
            name.
        """
        attrs = ('epoch', 'ver', 'rel', 'arch')
        return util.make_evra(*[getattr(self, '_'+a) for a in attrs])
    @property
    def rpm_path(self):
        """
        :returns: Absolute path to rpm package.
        """
        return self._rpm_path

    @property
    def summary(self):
        """
        :returns: Package summary string.
        """
        return subprocess.check_output(
                ['/usr/bin/rpm', '-q', '--qf', '%{SUMMARY}', '-p',
                    self.rpm_path])

    def get_nevra(self, with_epoch='NOT_ZERO'):
        """
        :param string with_epoch: Says when the epoch part should appear
            in resulting string. There are following possible values:

            * NOT_ZERO - epoch shall be present if it's greater than 0
            * ALWAYS - epoch will be present
            * NEVER - epoch won't be present

        :returns: Package nevra string.
        :rtype: string
        """
        attrs = ('name', 'epoch', 'ver', 'rel', 'arch')
        return util.make_nevra(*[getattr(self, '_'+a) for a in attrs],
                with_epoch=with_epoch)

    def __contains__(self, path):
        return path in self._files
    def __iter__(self):
        for pkg in self._files.__iter__():
            yield pkg
    def __len__(self):
        return len(self._files)
    @property
    def files(self):
        """
        :returns: Set of files and directories installed by this package.
        :rtype: set
        """
        return self._files.copy()

def to_json(_encoder, pkg):
    """
    Converts package object to dictionary which json encoder can handle.

    :param _encoder: Instance of json encoder.
    :param pkg: Package object to convert.
    :type pkg: :py:class:`Package`
    :returns: Dictionary with package attributes.
    :rtype: dictionary
    """
    if not isinstance(pkg, Package):
        raise TypeError("not a Package object")
    return pkg.__dict__

def from_json(json_object):
    """
    Constructs a package object from dictionary loaded from json text.
    Inverse function to :py:func:`to_json`.

    :param dictionary json_object: Deserialized package as a dictionary.
    :returns: Package object.
    :rtype: :py:class:`Package`
    """
    if isinstance(json_object, dict) and '_arch' in json_object:
        kwargs = {k[1:]: v for k, v in json_object.items()}
        return Package(**kwargs)
    return json_object

def is_pkg_installed(pkg):
    """
    Check, whether package is installed.
    """
    if not isinstance(pkg, Package):
        return subprocess.call(["rpm", "--quiet", "-q", pkg]) == 0
    else:
        cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}\n", pkg.get_nevra(
            with_epoch='NEVER') ]
        try:
            out = subprocess.check_output(cmd).splitlines()[0]
            epoch, _ = out.split(':')
            if not epoch or epoch.lower() == "(none)":
                epoch = "0"
            return int(epoch) == int(pkg.epoch)
        except subprocess.CalledProcessError:
            return False

def remove_pkg(pkg, *args):
    """
    Remove package with rpm command.

    :param pkg: Either an instance of :py:class:`Package` or package name.
        If it's a name, any version will be removed. Otherwise the exact
        version must be installed for command to be successful.
    :param list args: List of parameters for rpm command.
    """
    cmd = ["rpm", "--quiet"] + list(args)
    if isinstance(pkg, Package):
        cmd.extend(["-e", pkg.get_nevra()])
    else:
        cmd.extend(["-e", pkg])
    subprocess.call(cmd)

def install_pkg(pkg):
    """
    Install a specific package.

    :param pkg: Package object.
    :type pkg: :py:class:`Package`
    """
    if not isinstance(pkg, Package):
        raise TypeError("pkg must be a Package instance")
    subprocess.call(["rpm", "--quiet", "-i", pkg.rpm_path])