virt.virt_test_utils: run_autotest - 'tar' needs relative paths to strip the leading '/'
[autotest-zwu.git] / client / bin / package.py
blob9a836b6a2cad995eae0e85d78808b78c5c823d96
1 """
2 Functions to handle software packages. The functions covered here aim to be
3 generic, with implementations that deal with different package managers, such
4 as dpkg and rpm.
5 """
7 __author__ = 'lucasmr@br.ibm.com (Lucas Meneghel Rodrigues)'
9 import os, re
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):
18 """\
19 Private function that returns a dictionary with information about an
20 RPM package file
21 - type: Package management program that handles the file
22 - system_support: If the package management program is installed on the
23 system or not
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
29 (False)
30 """
31 # We will make good use of what the file command has to tell us about the
32 # package :)
33 file_result = utils.system_output('file ' + rpm_package)
34 package_info = {}
35 package_info['type'] = 'rpm'
36 try:
37 os_dep.command('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
53 else:
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
58 try:
59 utils.system(i_cmd)
60 package_info['installed'] = True
61 except:
62 package_info['installed'] = False
64 except:
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
77 else:
78 package_info['source'] = False
79 # Get architecture
80 package_info['arch'] = file_result.split(' ')[4]
81 # Get version
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
89 else:
90 package_info['source'] = False
91 # When the arch param is missing on file, we assume noarch
92 package_info['arch'] = 'noarch'
93 # Get version
94 package_info['version'] = file_result.split(' ')[4]
95 else:
96 # If everything else fails...
97 package_info['source'] = False
98 package_info['arch'] = 'Not Available'
99 package_info['version'] = 'Not Available'
100 return package_info
103 def _dpkg_info(dpkg_package):
104 """\
105 Private function that returns a dictionary with information about a
106 dpkg package file
107 - type: Package management program that handles the file
108 - system_support: If the package management program is installed on the
109 system or not
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
115 (False)
117 # We will make good use of what the file command has to tell us about the
118 # package :)
119 file_result = utils.system_output('file ' + dpkg_package)
120 package_info = {}
121 package_info['type'] = 'dpkg'
122 # There's no single debian source package as is the case
123 # with RPM
124 package_info['source'] = False
125 try:
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
144 else:
145 package_info['installed'] = True
147 except:
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'
155 return package_info
158 def list_all():
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:
169 parts = line.split()
170 if parts[0] == "ii": # only grab "installed" packages
171 installed_packages.append("%s-%s" % (parts[1], parts[2]))
173 return installed_packages
176 def info(package):
177 """\
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
181 system or not
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
187 (False)
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
193 package types.
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):
218 """\
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'
231 % (type, package))
232 raise error.PackageError(e_msg)
234 opt_args = ''
235 if type == 'rpm':
236 if nodeps:
237 opt_args = opt_args + '--nodeps'
238 install_command = 'rpm %s -U %s' % (opt_args, package)
239 if type == 'dpkg':
240 if nodeps:
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
245 # with this check
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
251 # least for now.
252 utils.system(install_command)
253 return 'Package %s was installed successfuly' % package
256 def convert(package, destination_format):
257 """\
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.
262 try:
263 os_dep.command('alien')
264 except:
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'
273 % package)
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'
278 % package)
279 converted_package = re.findall(rpm_pattern, conv_output)[0]
280 else:
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)
289 def os_support():
290 """\
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),
295 or False otherwise
297 support_info = {}
298 for package_manager in KNOWN_PACKAGE_MANAGERS:
299 try:
300 os_dep.command(package_manager)
301 support_info[package_manager] = True
302 except:
303 support_info[package_manager] = False
305 try:
306 os_dep.command('alien')
307 support_info['conversion'] = True
308 except:
309 support_info['conversion'] = False
311 return support_info