2 Functions to handle software packages. The functions covered here aim to be
3 generic, with implementations that deal with different package managers, such
7 __author__
= 'lucasmr@br.ibm.com (Lucas Meneghel Rodrigues)'
10 from autotest_lib
.client
.bin
import os_dep
, utils
11 from autotest_lib
.client
.common_lib
import error
13 # As more package methods are implemented, this list grows up
14 KNOWN_PACKAGE_MANAGERS
= ['rpm', 'dpkg']
17 def _rpm_info(rpm_package
):
19 Private function that returns a dictionary with information about an
21 - type: Package management program that handles the file
22 - system_support: If the package management program is installed on the
24 - source: If it is a source (True) our binary (False) package
25 - version: The package version (or name), that is used to check against the
26 package manager if the package is installed
27 - arch: The architecture for which a binary package was built
28 - installed: Whether the package is installed (True) on the system or not
31 # We will make good use of what the file command has to tell us about the
33 file_result
= utils
.system_output('file ' + rpm_package
)
35 package_info
['type'] = 'rpm'
38 # Build the command strings that will be used to get package info
39 # s_cmd - Command to determine if package is a source package
40 # a_cmd - Command to determine package architecture
41 # v_cmd - Command to determine package version
42 # i_cmd - Command to determiine if package is installed
43 s_cmd
= 'rpm -qp --qf %{SOURCE} ' + rpm_package
+ ' 2>/dev/null'
44 a_cmd
= 'rpm -qp --qf %{ARCH} ' + rpm_package
+ ' 2>/dev/null'
45 v_cmd
= 'rpm -qp ' + rpm_package
+ ' 2>/dev/null'
46 i_cmd
= 'rpm -q ' + utils
.system_output(v_cmd
) + ' 2>&1 >/dev/null'
48 package_info
['system_support'] = True
49 # Checking whether this is a source or src package
50 source
= utils
.system_output(s_cmd
)
51 if source
== '(none)':
52 package_info
['source'] = False
54 package_info
['source'] = True
55 package_info
['version'] = utils
.system_output(v_cmd
)
56 package_info
['arch'] = utils
.system_output(a_cmd
)
57 # Checking if package is installed
60 package_info
['installed'] = True
62 package_info
['installed'] = False
65 package_info
['system_support'] = False
66 package_info
['installed'] = False
67 # File gives a wealth of information about rpm packages.
68 # However, we can't trust all this info, as incorrectly
69 # packaged rpms can report some wrong values.
70 # It's better than nothing though :)
71 if len(file_result
.split(' ')) == 6:
72 # Figure if package is a source package
73 if file_result
.split(' ')[3] == 'src':
74 package_info
['source'] = True
75 elif file_result
.split(' ')[3] == 'bin':
76 package_info
['source'] = False
78 package_info
['source'] = False
80 package_info
['arch'] = file_result
.split(' ')[4]
82 package_info
['version'] = file_result
.split(' ')[5]
83 elif len(file_result
.split(' ')) == 5:
84 # Figure if package is a source package
85 if file_result
.split(' ')[3] == 'src':
86 package_info
['source'] = True
87 elif file_result
.split(' ')[3] == 'bin':
88 package_info
['source'] = False
90 package_info
['source'] = False
91 # When the arch param is missing on file, we assume noarch
92 package_info
['arch'] = 'noarch'
94 package_info
['version'] = file_result
.split(' ')[4]
96 # If everything else fails...
97 package_info
['source'] = False
98 package_info
['arch'] = 'Not Available'
99 package_info
['version'] = 'Not Available'
103 def _dpkg_info(dpkg_package
):
105 Private function that returns a dictionary with information about a
107 - type: Package management program that handles the file
108 - system_support: If the package management program is installed on the
110 - source: If it is a source (True) our binary (False) package
111 - version: The package version (or name), that is used to check against the
112 package manager if the package is installed
113 - arch: The architecture for which a binary package was built
114 - installed: Whether the package is installed (True) on the system or not
117 # We will make good use of what the file command has to tell us about the
119 file_result
= utils
.system_output('file ' + dpkg_package
)
121 package_info
['type'] = 'dpkg'
122 # There's no single debian source package as is the case
124 package_info
['source'] = False
126 os_dep
.command('dpkg')
127 # Build the command strings that will be used to get package info
128 # a_cmd - Command to determine package architecture
129 # v_cmd - Command to determine package version
130 # i_cmd - Command to determiine if package is installed
131 a_cmd
= 'dpkg -f ' + dpkg_package
+ ' Architecture 2>/dev/null'
132 v_cmd
= 'dpkg -f ' + dpkg_package
+ ' Package 2>/dev/null'
133 i_cmd
= 'dpkg -s ' + utils
.system_output(v_cmd
) + ' 2>/dev/null'
135 package_info
['system_support'] = True
136 package_info
['version'] = utils
.system_output(v_cmd
)
137 package_info
['arch'] = utils
.system_output(a_cmd
)
138 # Checking if package is installed
139 package_status
= utils
.system_output(i_cmd
, ignore_status
=True)
140 not_inst_pattern
= re
.compile('not-installed', re
.IGNORECASE
)
141 dpkg_not_installed
= re
.search(not_inst_pattern
, package_status
)
142 if dpkg_not_installed
:
143 package_info
['installed'] = False
145 package_info
['installed'] = True
148 package_info
['system_support'] = False
149 package_info
['installed'] = False
150 # The output of file is not as generous for dpkg files as
151 # it is with rpm files
152 package_info
['arch'] = 'Not Available'
153 package_info
['version'] = 'Not Available'
159 """Returns a list with the names of all currently installed packages."""
160 support_info
= os_support()
161 installed_packages
= []
163 if support_info
['rpm']:
164 installed_packages
+= utils
.system_output('rpm -qa').splitlines()
166 if support_info
['dpkg']:
167 raw_list
= utils
.system_output('dpkg -l').splitlines()[5:]
168 for line
in raw_list
:
170 if parts
[0] == "ii": # only grab "installed" packages
171 installed_packages
.append("%s-%s" % (parts
[1], parts
[2]))
173 return installed_packages
178 Returns a dictionary with package information about a given package file:
179 - type: Package management program that handles the file
180 - system_support: If the package management program is installed on the
182 - source: If it is a source (True) our binary (False) package
183 - version: The package version (or name), that is used to check against the
184 package manager if the package is installed
185 - arch: The architecture for which a binary package was built
186 - installed: Whether the package is installed (True) on the system or not
189 Implemented package types:
190 - 'dpkg' - dpkg (debian, ubuntu) package files
191 - 'rpm' - rpm (red hat, suse) package files
192 Raises an exception if the package type is not one of the implemented
195 if not os
.path
.isfile(package
):
196 raise ValueError('invalid file %s to verify' % package
)
197 # Use file and libmagic to determine the actual package file type.
198 file_result
= utils
.system_output('file ' + package
)
199 for package_manager
in KNOWN_PACKAGE_MANAGERS
:
200 if package_manager
== 'rpm':
201 package_pattern
= re
.compile('RPM', re
.IGNORECASE
)
202 elif package_manager
== 'dpkg':
203 package_pattern
= re
.compile('Debian', re
.IGNORECASE
)
205 result
= re
.search(package_pattern
, file_result
)
207 if result
and package_manager
== 'rpm':
208 return _rpm_info(package
)
209 elif result
and package_manager
== 'dpkg':
210 return _dpkg_info(package
)
212 # If it's not one of the implemented package manager methods, there's
213 # not much that can be done, hence we throw an exception.
214 raise error
.PackageError('Unknown package type %s' % file_result
)
217 def install(package
, nodeps
= False):
219 Tries to install a package file. If the package is already installed,
220 it prints a message to the user and ends gracefully. If nodeps is set to
221 true, it will ignore package dependencies.
223 my_package_info
= info(package
)
224 type = my_package_info
['type']
225 system_support
= my_package_info
['system_support']
226 source
= my_package_info
['source']
227 installed
= my_package_info
['installed']
229 if not system_support
:
230 e_msg
= ('Client does not have package manager %s to handle %s install'
232 raise error
.PackageError(e_msg
)
237 opt_args
= opt_args
+ '--nodeps'
238 install_command
= 'rpm %s -U %s' % (opt_args
, package
)
241 opt_args
= opt_args
+ '--force-depends'
242 install_command
= 'dpkg %s -i %s' % (opt_args
, package
)
244 # RPM source packages can be installed along with the binary versions
246 if installed
and not source
:
247 return 'Package %s is already installed' % package
249 # At this point, the most likely thing to go wrong is that there are
250 # unmet dependencies for the package. We won't cover this case, at
252 utils
.system(install_command
)
253 return 'Package %s was installed successfuly' % package
256 def convert(package
, destination_format
):
258 Convert packages with the 'alien' utility. If alien is not installed, it
259 throws a NotImplementedError exception.
260 returns: filename of the package generated.
263 os_dep
.command('alien')
265 e_msg
= 'Cannot convert to %s, alien not installed' % destination_format
266 raise error
.TestError(e_msg
)
268 # alien supports converting to many formats, but its interesting to map
269 # convertions only for the implemented package types.
270 if destination_format
== 'dpkg':
271 deb_pattern
= re
.compile('[A-Za-z0-9_.-]*[.][d][e][b]')
272 conv_output
= utils
.system_output('alien --to-deb %s 2>/dev/null'
274 converted_package
= re
.findall(deb_pattern
, conv_output
)[0]
275 elif destination_format
== 'rpm':
276 rpm_pattern
= re
.compile('[A-Za-z0-9_.-]*[.][r][p][m]')
277 conv_output
= utils
.system_output('alien --to-rpm %s 2>/dev/null'
279 converted_package
= re
.findall(rpm_pattern
, conv_output
)[0]
281 e_msg
= 'Convertion to format %s not implemented' % destination_format
282 raise NotImplementedError(e_msg
)
284 print 'Package %s successfuly converted to %s' % \
285 (os
.path
.basename(package
), os
.path
.basename(converted_package
))
286 return os
.path
.abspath(converted_package
)
291 Returns a dictionary with host os package support info:
292 - rpm: True if system supports rpm packages, False otherwise
293 - dpkg: True if system supports dpkg packages, False otherwise
294 - conversion: True if the system can convert packages (alien installed),
298 for package_manager
in KNOWN_PACKAGE_MANAGERS
:
300 os_dep
.command(package_manager
)
301 support_info
[package_manager
] = True
303 support_info
[package_manager
] = False
306 os_dep
.command('alien')
307 support_info
['conversion'] = True
309 support_info
['conversion'] = False