ronnie
2022-10-14 1504bb53e29d3d46222c0b3ea994fc494b48e153
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
"""
Functions to handle software packages. The functions covered here aim to be
generic, with implementations that deal with different package managers, such
as dpkg and rpm.
"""
 
__author__ = 'lucasmr@br.ibm.com (Lucas Meneghel Rodrigues)'
 
import os, re
from autotest_lib.client.bin import os_dep, utils
from autotest_lib.client.common_lib import error
 
# As more package methods are implemented, this list grows up
KNOWN_PACKAGE_MANAGERS = ['rpm', 'dpkg']
 
 
def _rpm_info(rpm_package):
    """\
    Private function that returns a dictionary with information about an
    RPM package file
    - type: Package management program that handles the file
    - system_support: If the package management program is installed on the
    system or not
    - source: If it is a source (True) our binary (False) package
    - version: The package version (or name), that is used to check against the
    package manager if the package is installed
    - arch: The architecture for which a binary package was built
    - installed: Whether the package is installed (True) on the system or not
    (False)
    """
    # We will make good use of what the file command has to tell us about the
    # package :)
    file_result = utils.system_output('file ' + rpm_package)
    package_info = {}
    package_info['type'] = 'rpm'
    try:
        os_dep.command('rpm')
        # Build the command strings that will be used to get package info
        # s_cmd - Command to determine if package is a source package
        # a_cmd - Command to determine package architecture
        # v_cmd - Command to determine package version
        # i_cmd - Command to determiine if package is installed
        s_cmd = 'rpm -qp --qf %{SOURCE} ' + rpm_package + ' 2>/dev/null'
        a_cmd = 'rpm -qp --qf %{ARCH} ' + rpm_package + ' 2>/dev/null'
        v_cmd = 'rpm -qp ' + rpm_package + ' 2>/dev/null'
        i_cmd = 'rpm -q ' + utils.system_output(v_cmd) + ' 2>&1 >/dev/null'
 
        package_info['system_support'] = True
        # Checking whether this is a source or src package
        source = utils.system_output(s_cmd)
        if source == '(none)':
            package_info['source'] = False
        else:
            package_info['source'] = True
        package_info['version'] = utils.system_output(v_cmd)
        package_info['arch'] = utils.system_output(a_cmd)
        # Checking if package is installed
        try:
            utils.system(i_cmd)
            package_info['installed'] = True
        except:
            package_info['installed'] = False
 
    except:
        package_info['system_support'] = False
        package_info['installed'] = False
        # File gives a wealth of information about rpm packages.
        # However, we can't trust all this info, as incorrectly
        # packaged rpms can report some wrong values.
        # It's better than nothing though :)
        if len(file_result.split(' ')) == 6:
            # Figure if package is a source package
            if file_result.split(' ')[3] == 'src':
                package_info['source'] = True
            elif file_result.split(' ')[3] == 'bin':
                package_info['source'] = False
            else:
                package_info['source'] = False
            # Get architecture
            package_info['arch'] = file_result.split(' ')[4]
            # Get version
            package_info['version'] = file_result.split(' ')[5]
        elif len(file_result.split(' ')) == 5:
            # Figure if package is a source package
            if file_result.split(' ')[3] == 'src':
                package_info['source'] = True
            elif file_result.split(' ')[3] == 'bin':
                package_info['source'] = False
            else:
                package_info['source'] = False
            # When the arch param is missing on file, we assume noarch
            package_info['arch'] = 'noarch'
            # Get version
            package_info['version'] = file_result.split(' ')[4]
        else:
            # If everything else fails...
            package_info['source'] =  False
            package_info['arch'] = 'Not Available'
            package_info['version'] = 'Not Available'
    return package_info
 
 
def _dpkg_info(dpkg_package):
    """\
    Private function that returns a dictionary with information about a
    dpkg package file
    - type: Package management program that handles the file
    - system_support: If the package management program is installed on the
    system or not
    - source: If it is a source (True) our binary (False) package
    - version: The package version (or name), that is used to check against the
    package manager if the package is installed
    - arch: The architecture for which a binary package was built
    - installed: Whether the package is installed (True) on the system or not
    (False)
    """
    # We will make good use of what the file command has to tell us about the
    # package :)
    file_result = utils.system_output('file ' + dpkg_package)
    package_info = {}
    package_info['type'] = 'dpkg'
    # There's no single debian source package as is the case
    # with RPM
    package_info['source'] = False
    try:
        os_dep.command('dpkg')
        # Build the command strings that will be used to get package info
        # a_cmd - Command to determine package architecture
        # v_cmd - Command to determine package version
        # i_cmd - Command to determiine if package is installed
        a_cmd = 'dpkg -f ' + dpkg_package + ' Architecture 2>/dev/null'
        v_cmd = 'dpkg -f ' + dpkg_package + ' Package 2>/dev/null'
        i_cmd = 'dpkg -s ' + utils.system_output(v_cmd) + ' 2>/dev/null'
 
        package_info['system_support'] = True
        package_info['version'] = utils.system_output(v_cmd)
        package_info['arch'] = utils.system_output(a_cmd)
        # Checking if package is installed
        package_status = utils.system_output(i_cmd, ignore_status=True)
        not_inst_pattern = re.compile('not-installed', re.IGNORECASE)
        dpkg_not_installed = re.search(not_inst_pattern, package_status)
        if dpkg_not_installed:
            package_info['installed'] = False
        else:
            package_info['installed'] = True
 
    except:
        package_info['system_support'] = False
        package_info['installed'] = False
        # The output of file is not as generous for dpkg files as
        # it is with rpm files
        package_info['arch'] = 'Not Available'
        package_info['version'] = 'Not Available'
 
    return package_info
 
 
def list_all():
    """Returns a list with the names of all currently installed packages."""
    support_info = os_support()
    installed_packages = []
 
    if support_info['rpm']:
        installed_packages += utils.system_output('rpm -qa').splitlines()
 
    if support_info['dpkg']:
        raw_list = utils.system_output('dpkg -l').splitlines()[5:]
        for line in raw_list:
            parts = line.split()
            if parts[0] == "ii":  # only grab "installed" packages
                installed_packages.append("%s-%s" % (parts[1], parts[2]))
 
    return installed_packages
 
 
def info(package):
    """\
    Returns a dictionary with package information about a given package file:
    - type: Package management program that handles the file
    - system_support: If the package management program is installed on the
    system or not
    - source: If it is a source (True) our binary (False) package
    - version: The package version (or name), that is used to check against the
    package manager if the package is installed
    - arch: The architecture for which a binary package was built
    - installed: Whether the package is installed (True) on the system or not
    (False)
 
    Implemented package types:
    - 'dpkg' - dpkg (debian, ubuntu) package files
    - 'rpm' - rpm (red hat, suse) package files
    Raises an exception if the package type is not one of the implemented
    package types.
    """
    if not os.path.isfile(package):
        raise ValueError('invalid file %s to verify' % package)
    # Use file and libmagic to determine the actual package file type.
    file_result = utils.system_output('file ' + package)
    for package_manager in KNOWN_PACKAGE_MANAGERS:
        if package_manager == 'rpm':
            package_pattern = re.compile('RPM', re.IGNORECASE)
        elif package_manager == 'dpkg':
            package_pattern = re.compile('Debian', re.IGNORECASE)
 
        result = re.search(package_pattern, file_result)
 
        if result and package_manager == 'rpm':
            return _rpm_info(package)
        elif result and package_manager == 'dpkg':
            return _dpkg_info(package)
 
    # If it's not one of the implemented package manager methods, there's
    # not much that can be done, hence we throw an exception.
    raise error.PackageError('Unknown package type %s' % file_result)
 
 
def install(package, nodeps = False):
    """\
    Tries to install a package file. If the package is already installed,
    it prints a message to the user and ends gracefully. If nodeps is set to
    true, it will ignore package dependencies.
    """
    my_package_info = info(package)
    type = my_package_info['type']
    system_support = my_package_info['system_support']
    source = my_package_info['source']
    installed = my_package_info['installed']
 
    if not system_support:
        e_msg = ('Client does not have package manager %s to handle %s install'
                 % (type, package))
        raise error.PackageError(e_msg)
 
    opt_args = ''
    if type == 'rpm':
        if nodeps:
            opt_args = opt_args + '--nodeps'
        install_command = 'rpm %s -U %s' % (opt_args, package)
    if type == 'dpkg':
        if nodeps:
            opt_args = opt_args + '--force-depends'
        install_command = 'dpkg %s -i %s' % (opt_args, package)
 
    # RPM source packages can be installed along with the binary versions
    # with this check
    if installed and not source:
        return 'Package %s is already installed' % package
 
    # At this point, the most likely thing to go wrong is that there are
    # unmet dependencies for the package. We won't cover this case, at
    # least for now.
    utils.system(install_command)
    return 'Package %s was installed successfuly' % package
 
 
def convert(package, destination_format):
    """\
    Convert packages with the 'alien' utility. If alien is not installed, it
    throws a NotImplementedError exception.
    returns: filename of the package generated.
    """
    try:
        os_dep.command('alien')
    except:
        e_msg = 'Cannot convert to %s, alien not installed' % destination_format
        raise error.TestError(e_msg)
 
    # alien supports converting to many formats, but its interesting to map
    # convertions only for the implemented package types.
    if destination_format == 'dpkg':
        deb_pattern = re.compile('[A-Za-z0-9_.-]*[.][d][e][b]')
        conv_output = utils.system_output('alien --to-deb %s 2>/dev/null'
                                          % package)
        converted_package = re.findall(deb_pattern, conv_output)[0]
    elif destination_format == 'rpm':
        rpm_pattern = re.compile('[A-Za-z0-9_.-]*[.][r][p][m]')
        conv_output = utils.system_output('alien --to-rpm %s 2>/dev/null'
                                          % package)
        converted_package = re.findall(rpm_pattern, conv_output)[0]
    else:
        e_msg = 'Convertion to format %s not implemented' % destination_format
        raise NotImplementedError(e_msg)
 
    print 'Package %s successfuly converted to %s' % \
            (os.path.basename(package), os.path.basename(converted_package))
    return os.path.abspath(converted_package)
 
 
def os_support():
    """\
    Returns a dictionary with host os package support info:
    - rpm: True if system supports rpm packages, False otherwise
    - dpkg: True if system supports dpkg packages, False otherwise
    - conversion: True if the system can convert packages (alien installed),
    or False otherwise
    """
    support_info = {}
    for package_manager in KNOWN_PACKAGE_MANAGERS:
        try:
            os_dep.command(package_manager)
            support_info[package_manager] = True
        except:
            support_info[package_manager] = False
 
    try:
        os_dep.command('alien')
        support_info['conversion'] = True
    except:
        support_info['conversion'] = False
 
    return support_info