xref: /aosp_15_r20/external/autotest/client/common_lib/software_manager.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/python3
2*9c5db199SXin Li"""
3*9c5db199SXin LiSoftware package management library.
4*9c5db199SXin Li
5*9c5db199SXin LiThis is an abstraction layer on top of the existing distributions high level
6*9c5db199SXin Lipackage managers. It supports package operations useful for testing purposes,
7*9c5db199SXin Liand multiple high level package managers (here called backends). If you want
8*9c5db199SXin Lito make this lib to support your particular package manager/distro, please
9*9c5db199SXin Liimplement the given backend class.
10*9c5db199SXin Li
11*9c5db199SXin Li@author: Higor Vieira Alves ([email protected])
12*9c5db199SXin Li@author: Lucas Meneghel Rodrigues ([email protected])
13*9c5db199SXin Li@author: Ramon de Carvalho Valle ([email protected])
14*9c5db199SXin Li
15*9c5db199SXin Li@copyright: IBM 2008-2009
16*9c5db199SXin Li@copyright: Red Hat 2009-2010
17*9c5db199SXin Li"""
18*9c5db199SXin Liimport os, re, logging, optparse, random, string
19*9c5db199SXin Litry:
20*9c5db199SXin Li    import yum
21*9c5db199SXin Liexcept:
22*9c5db199SXin Li    pass
23*9c5db199SXin Liimport common
24*9c5db199SXin Li
25*9c5db199SXin Lifrom autotest_lib.client.bin import os_dep, utils
26*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
27*9c5db199SXin Lifrom autotest_lib.client.common_lib import logging_config, logging_manager
28*9c5db199SXin Lifrom autotest_lib.client.common_lib import seven
29*9c5db199SXin Li
30*9c5db199SXin Li
31*9c5db199SXin Lidef generate_random_string(length):
32*9c5db199SXin Li    """
33*9c5db199SXin Li    Return a random string using alphanumeric characters.
34*9c5db199SXin Li
35*9c5db199SXin Li    @length: Length of the string that will be generated.
36*9c5db199SXin Li    """
37*9c5db199SXin Li    r = random.SystemRandom()
38*9c5db199SXin Li    str = ""
39*9c5db199SXin Li    chars = string.letters + string.digits
40*9c5db199SXin Li    while length > 0:
41*9c5db199SXin Li        str += r.choice(chars)
42*9c5db199SXin Li        length -= 1
43*9c5db199SXin Li    return str
44*9c5db199SXin Li
45*9c5db199SXin Li
46*9c5db199SXin Liclass SoftwareManagerLoggingConfig(logging_config.LoggingConfig):
47*9c5db199SXin Li    """
48*9c5db199SXin Li    Used with the sole purpose of providing convenient logging setup
49*9c5db199SXin Li    for the KVM test auxiliary programs.
50*9c5db199SXin Li    """
51*9c5db199SXin Li    def configure_logging(self, results_dir=None, verbose=False):
52*9c5db199SXin Li        super(SoftwareManagerLoggingConfig, self).configure_logging(
53*9c5db199SXin Li                                                            use_console=True,
54*9c5db199SXin Li                                                            verbose=verbose)
55*9c5db199SXin Li
56*9c5db199SXin Li
57*9c5db199SXin Liclass SystemInspector(object):
58*9c5db199SXin Li    """
59*9c5db199SXin Li    System inspector class.
60*9c5db199SXin Li
61*9c5db199SXin Li    This may grow up to include more complete reports of operating system and
62*9c5db199SXin Li    machine properties.
63*9c5db199SXin Li    """
64*9c5db199SXin Li    def __init__(self):
65*9c5db199SXin Li        """
66*9c5db199SXin Li        Probe system, and save information for future reference.
67*9c5db199SXin Li        """
68*9c5db199SXin Li        self.distro = utils.get_os_vendor()
69*9c5db199SXin Li        self.high_level_pms = ['apt-get', 'yum', 'zypper']
70*9c5db199SXin Li
71*9c5db199SXin Li
72*9c5db199SXin Li    def get_package_management(self):
73*9c5db199SXin Li        """
74*9c5db199SXin Li        Determine the supported package management systems present on the
75*9c5db199SXin Li        system. If more than one package management system installed, try
76*9c5db199SXin Li        to find the best supported system.
77*9c5db199SXin Li        """
78*9c5db199SXin Li        list_supported = []
79*9c5db199SXin Li        for high_level_pm in self.high_level_pms:
80*9c5db199SXin Li            try:
81*9c5db199SXin Li                os_dep.command(high_level_pm)
82*9c5db199SXin Li                list_supported.append(high_level_pm)
83*9c5db199SXin Li            except:
84*9c5db199SXin Li                pass
85*9c5db199SXin Li
86*9c5db199SXin Li        pm_supported = None
87*9c5db199SXin Li        if len(list_supported) == 0:
88*9c5db199SXin Li            pm_supported = None
89*9c5db199SXin Li        if len(list_supported) == 1:
90*9c5db199SXin Li            pm_supported = list_supported[0]
91*9c5db199SXin Li        elif len(list_supported) > 1:
92*9c5db199SXin Li            if 'apt-get' in list_supported and self.distro in ['Debian', 'Ubuntu']:
93*9c5db199SXin Li                pm_supported = 'apt-get'
94*9c5db199SXin Li            elif 'yum' in list_supported and self.distro == 'Fedora':
95*9c5db199SXin Li                pm_supported = 'yum'
96*9c5db199SXin Li            else:
97*9c5db199SXin Li                pm_supported = list_supported[0]
98*9c5db199SXin Li
99*9c5db199SXin Li        logging.debug('Package Manager backend: %s' % pm_supported)
100*9c5db199SXin Li        return pm_supported
101*9c5db199SXin Li
102*9c5db199SXin Li
103*9c5db199SXin Liclass SoftwareManager(object):
104*9c5db199SXin Li    """
105*9c5db199SXin Li    Package management abstraction layer.
106*9c5db199SXin Li
107*9c5db199SXin Li    It supports a set of common package operations for testing purposes, and it
108*9c5db199SXin Li    uses the concept of a backend, a helper class that implements the set of
109*9c5db199SXin Li    operations of a given package management tool.
110*9c5db199SXin Li    """
111*9c5db199SXin Li    def __init__(self):
112*9c5db199SXin Li        """
113*9c5db199SXin Li        Class constructor.
114*9c5db199SXin Li
115*9c5db199SXin Li        Determines the best supported package management system for the given
116*9c5db199SXin Li        operating system running and initializes the appropriate backend.
117*9c5db199SXin Li        """
118*9c5db199SXin Li        inspector = SystemInspector()
119*9c5db199SXin Li        backend_type = inspector.get_package_management()
120*9c5db199SXin Li        if backend_type == 'yum':
121*9c5db199SXin Li            self.backend = YumBackend()
122*9c5db199SXin Li        elif backend_type == 'zypper':
123*9c5db199SXin Li            self.backend = ZypperBackend()
124*9c5db199SXin Li        elif backend_type == 'apt-get':
125*9c5db199SXin Li            self.backend = AptBackend()
126*9c5db199SXin Li        else:
127*9c5db199SXin Li            raise NotImplementedError('Unimplemented package management '
128*9c5db199SXin Li                                      'system: %s.' % backend_type)
129*9c5db199SXin Li
130*9c5db199SXin Li
131*9c5db199SXin Li    def check_installed(self, name, version=None, arch=None):
132*9c5db199SXin Li        """
133*9c5db199SXin Li        Check whether a package is installed on this system.
134*9c5db199SXin Li
135*9c5db199SXin Li        @param name: Package name.
136*9c5db199SXin Li        @param version: Package version.
137*9c5db199SXin Li        @param arch: Package architecture.
138*9c5db199SXin Li        """
139*9c5db199SXin Li        return self.backend.check_installed(name, version, arch)
140*9c5db199SXin Li
141*9c5db199SXin Li
142*9c5db199SXin Li    def list_all(self):
143*9c5db199SXin Li        """
144*9c5db199SXin Li        List all installed packages.
145*9c5db199SXin Li        """
146*9c5db199SXin Li        return self.backend.list_all()
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Li    def list_files(self, name):
150*9c5db199SXin Li        """
151*9c5db199SXin Li        Get a list of all files installed by package [name].
152*9c5db199SXin Li
153*9c5db199SXin Li        @param name: Package name.
154*9c5db199SXin Li        """
155*9c5db199SXin Li        return self.backend.list_files(name)
156*9c5db199SXin Li
157*9c5db199SXin Li
158*9c5db199SXin Li    def install(self, name):
159*9c5db199SXin Li        """
160*9c5db199SXin Li        Install package [name].
161*9c5db199SXin Li
162*9c5db199SXin Li        @param name: Package name.
163*9c5db199SXin Li        """
164*9c5db199SXin Li        return self.backend.install(name)
165*9c5db199SXin Li
166*9c5db199SXin Li
167*9c5db199SXin Li    def remove(self, name):
168*9c5db199SXin Li        """
169*9c5db199SXin Li        Remove package [name].
170*9c5db199SXin Li
171*9c5db199SXin Li        @param name: Package name.
172*9c5db199SXin Li        """
173*9c5db199SXin Li        return self.backend.remove(name)
174*9c5db199SXin Li
175*9c5db199SXin Li
176*9c5db199SXin Li    def add_repo(self, url):
177*9c5db199SXin Li        """
178*9c5db199SXin Li        Add package repo described by [url].
179*9c5db199SXin Li
180*9c5db199SXin Li        @param name: URL of the package repo.
181*9c5db199SXin Li        """
182*9c5db199SXin Li        return self.backend.add_repo(url)
183*9c5db199SXin Li
184*9c5db199SXin Li
185*9c5db199SXin Li    def remove_repo(self, url):
186*9c5db199SXin Li        """
187*9c5db199SXin Li        Remove package repo described by [url].
188*9c5db199SXin Li
189*9c5db199SXin Li        @param url: URL of the package repo.
190*9c5db199SXin Li        """
191*9c5db199SXin Li        return self.backend.remove_repo(url)
192*9c5db199SXin Li
193*9c5db199SXin Li
194*9c5db199SXin Li    def upgrade(self):
195*9c5db199SXin Li        """
196*9c5db199SXin Li        Upgrade all packages available.
197*9c5db199SXin Li        """
198*9c5db199SXin Li        return self.backend.upgrade()
199*9c5db199SXin Li
200*9c5db199SXin Li
201*9c5db199SXin Li    def provides(self, file):
202*9c5db199SXin Li        """
203*9c5db199SXin Li        Returns a list of packages that provides a given capability to the
204*9c5db199SXin Li        system (be it a binary, a library).
205*9c5db199SXin Li
206*9c5db199SXin Li        @param file: Path to the file.
207*9c5db199SXin Li        """
208*9c5db199SXin Li        return self.backend.provides(file)
209*9c5db199SXin Li
210*9c5db199SXin Li
211*9c5db199SXin Li    def install_what_provides(self, file):
212*9c5db199SXin Li        """
213*9c5db199SXin Li        Installs package that provides [file].
214*9c5db199SXin Li
215*9c5db199SXin Li        @param file: Path to file.
216*9c5db199SXin Li        """
217*9c5db199SXin Li        provides = self.provides(file)
218*9c5db199SXin Li        if provides is not None:
219*9c5db199SXin Li            self.install(provides)
220*9c5db199SXin Li        else:
221*9c5db199SXin Li            logging.warning('No package seems to provide %s', file)
222*9c5db199SXin Li
223*9c5db199SXin Li
224*9c5db199SXin Liclass RpmBackend(object):
225*9c5db199SXin Li    """
226*9c5db199SXin Li    This class implements operations executed with the rpm package manager.
227*9c5db199SXin Li
228*9c5db199SXin Li    rpm is a lower level package manager, used by higher level managers such
229*9c5db199SXin Li    as yum and zypper.
230*9c5db199SXin Li    """
231*9c5db199SXin Li    def __init__(self):
232*9c5db199SXin Li        self.lowlevel_base_cmd = os_dep.command('rpm')
233*9c5db199SXin Li
234*9c5db199SXin Li
235*9c5db199SXin Li    def _check_installed_version(self, name, version):
236*9c5db199SXin Li        """
237*9c5db199SXin Li        Helper for the check_installed public method.
238*9c5db199SXin Li
239*9c5db199SXin Li        @param name: Package name.
240*9c5db199SXin Li        @param version: Package version.
241*9c5db199SXin Li        """
242*9c5db199SXin Li        cmd = (self.lowlevel_base_cmd + ' -q --qf %{VERSION} ' + name +
243*9c5db199SXin Li               ' 2> /dev/null')
244*9c5db199SXin Li        inst_version = utils.system_output(cmd)
245*9c5db199SXin Li
246*9c5db199SXin Li        if inst_version >= version:
247*9c5db199SXin Li            return True
248*9c5db199SXin Li        else:
249*9c5db199SXin Li            return False
250*9c5db199SXin Li
251*9c5db199SXin Li
252*9c5db199SXin Li    def check_installed(self, name, version=None, arch=None):
253*9c5db199SXin Li        """
254*9c5db199SXin Li        Check if package [name] is installed.
255*9c5db199SXin Li
256*9c5db199SXin Li        @param name: Package name.
257*9c5db199SXin Li        @param version: Package version.
258*9c5db199SXin Li        @param arch: Package architecture.
259*9c5db199SXin Li        """
260*9c5db199SXin Li        if arch:
261*9c5db199SXin Li            cmd = (self.lowlevel_base_cmd + ' -q --qf %{ARCH} ' + name +
262*9c5db199SXin Li                   ' 2> /dev/null')
263*9c5db199SXin Li            inst_archs = utils.system_output(cmd)
264*9c5db199SXin Li            inst_archs = inst_archs.split('\n')
265*9c5db199SXin Li
266*9c5db199SXin Li            for inst_arch in inst_archs:
267*9c5db199SXin Li                if inst_arch == arch:
268*9c5db199SXin Li                    return self._check_installed_version(name, version)
269*9c5db199SXin Li            return False
270*9c5db199SXin Li
271*9c5db199SXin Li        elif version:
272*9c5db199SXin Li            return self._check_installed_version(name, version)
273*9c5db199SXin Li        else:
274*9c5db199SXin Li            cmd = 'rpm -q ' + name + ' 2> /dev/null'
275*9c5db199SXin Li            return (os.system(cmd) == 0)
276*9c5db199SXin Li
277*9c5db199SXin Li
278*9c5db199SXin Li    def list_all(self):
279*9c5db199SXin Li        """
280*9c5db199SXin Li        List all installed packages.
281*9c5db199SXin Li        """
282*9c5db199SXin Li        installed_packages = utils.system_output('rpm -qa').splitlines()
283*9c5db199SXin Li        return installed_packages
284*9c5db199SXin Li
285*9c5db199SXin Li
286*9c5db199SXin Li    def list_files(self, name):
287*9c5db199SXin Li        """
288*9c5db199SXin Li        List files installed on the system by package [name].
289*9c5db199SXin Li
290*9c5db199SXin Li        @param name: Package name.
291*9c5db199SXin Li        """
292*9c5db199SXin Li        path = os.path.abspath(name)
293*9c5db199SXin Li        if os.path.isfile(path):
294*9c5db199SXin Li            option = '-qlp'
295*9c5db199SXin Li            name = path
296*9c5db199SXin Li        else:
297*9c5db199SXin Li            option = '-ql'
298*9c5db199SXin Li
299*9c5db199SXin Li        l_cmd = 'rpm' + ' ' + option + ' ' + name + ' 2> /dev/null'
300*9c5db199SXin Li
301*9c5db199SXin Li        try:
302*9c5db199SXin Li            result = utils.system_output(l_cmd)
303*9c5db199SXin Li            list_files = result.split('\n')
304*9c5db199SXin Li            return list_files
305*9c5db199SXin Li        except error.CmdError:
306*9c5db199SXin Li            return []
307*9c5db199SXin Li
308*9c5db199SXin Li
309*9c5db199SXin Liclass DpkgBackend(object):
310*9c5db199SXin Li    """
311*9c5db199SXin Li    This class implements operations executed with the dpkg package manager.
312*9c5db199SXin Li
313*9c5db199SXin Li    dpkg is a lower level package manager, used by higher level managers such
314*9c5db199SXin Li    as apt and aptitude.
315*9c5db199SXin Li    """
316*9c5db199SXin Li    def __init__(self):
317*9c5db199SXin Li        self.lowlevel_base_cmd = os_dep.command('dpkg')
318*9c5db199SXin Li
319*9c5db199SXin Li
320*9c5db199SXin Li    def check_installed(self, name):
321*9c5db199SXin Li        if os.path.isfile(name):
322*9c5db199SXin Li            n_cmd = (self.lowlevel_base_cmd + ' -f ' + name +
323*9c5db199SXin Li                     ' Package 2>/dev/null')
324*9c5db199SXin Li            name = utils.system_output(n_cmd)
325*9c5db199SXin Li        i_cmd = self.lowlevel_base_cmd + ' -s ' + name + ' 2>/dev/null'
326*9c5db199SXin Li        # Checking if package is installed
327*9c5db199SXin Li        package_status = utils.system_output(i_cmd, ignore_status=True)
328*9c5db199SXin Li        not_inst_pattern = re.compile('not-installed', re.IGNORECASE)
329*9c5db199SXin Li        dpkg_not_installed = re.search(not_inst_pattern, package_status)
330*9c5db199SXin Li        if dpkg_not_installed:
331*9c5db199SXin Li            return False
332*9c5db199SXin Li        return True
333*9c5db199SXin Li
334*9c5db199SXin Li
335*9c5db199SXin Li    def list_all(self):
336*9c5db199SXin Li        """
337*9c5db199SXin Li        List all packages available in the system.
338*9c5db199SXin Li        """
339*9c5db199SXin Li        installed_packages = []
340*9c5db199SXin Li        raw_list = utils.system_output('dpkg -l').splitlines()[5:]
341*9c5db199SXin Li        for line in raw_list:
342*9c5db199SXin Li            parts = line.split()
343*9c5db199SXin Li            if parts[0] == "ii":  # only grab "installed" packages
344*9c5db199SXin Li                installed_packages.append("%s-%s" % (parts[1], parts[2]))
345*9c5db199SXin Li
346*9c5db199SXin Li
347*9c5db199SXin Li    def list_files(self, package):
348*9c5db199SXin Li        """
349*9c5db199SXin Li        List files installed by package [package].
350*9c5db199SXin Li
351*9c5db199SXin Li        @param package: Package name.
352*9c5db199SXin Li        @return: List of paths installed by package.
353*9c5db199SXin Li        """
354*9c5db199SXin Li        if os.path.isfile(package):
355*9c5db199SXin Li            l_cmd = self.lowlevel_base_cmd + ' -c ' + package
356*9c5db199SXin Li        else:
357*9c5db199SXin Li            l_cmd = self.lowlevel_base_cmd + ' -l ' + package
358*9c5db199SXin Li        return utils.system_output(l_cmd).split('\n')
359*9c5db199SXin Li
360*9c5db199SXin Li
361*9c5db199SXin Liclass YumBackend(RpmBackend):
362*9c5db199SXin Li    """
363*9c5db199SXin Li    Implements the yum backend for software manager.
364*9c5db199SXin Li
365*9c5db199SXin Li    Set of operations for the yum package manager, commonly found on Yellow Dog
366*9c5db199SXin Li    Linux and Red Hat based distributions, such as Fedora and Red Hat
367*9c5db199SXin Li    Enterprise Linux.
368*9c5db199SXin Li    """
369*9c5db199SXin Li    def __init__(self):
370*9c5db199SXin Li        """
371*9c5db199SXin Li        Initializes the base command and the yum package repository.
372*9c5db199SXin Li        """
373*9c5db199SXin Li        super(YumBackend, self).__init__()
374*9c5db199SXin Li        executable = os_dep.command('yum')
375*9c5db199SXin Li        base_arguments = '-y'
376*9c5db199SXin Li        self.base_command = executable + ' ' + base_arguments
377*9c5db199SXin Li        self.repo_file_path = '/etc/yum.repos.d/autotest.repo'
378*9c5db199SXin Li        self.cfgparser = seven.config_parser()
379*9c5db199SXin Li        self.cfgparser.read(self.repo_file_path)
380*9c5db199SXin Li        y_cmd = executable + ' --version | head -1'
381*9c5db199SXin Li        self.yum_version = utils.system_output(y_cmd, ignore_status=True)
382*9c5db199SXin Li        logging.debug('Yum backend initialized')
383*9c5db199SXin Li        logging.debug('Yum version: %s' % self.yum_version)
384*9c5db199SXin Li        self.yum_base = yum.YumBase()
385*9c5db199SXin Li
386*9c5db199SXin Li
387*9c5db199SXin Li    def _cleanup(self):
388*9c5db199SXin Li        """
389*9c5db199SXin Li        Clean up the yum cache so new package information can be downloaded.
390*9c5db199SXin Li        """
391*9c5db199SXin Li        utils.system("yum clean all")
392*9c5db199SXin Li
393*9c5db199SXin Li
394*9c5db199SXin Li    def install(self, name):
395*9c5db199SXin Li        """
396*9c5db199SXin Li        Installs package [name]. Handles local installs.
397*9c5db199SXin Li        """
398*9c5db199SXin Li        if os.path.isfile(name):
399*9c5db199SXin Li            name = os.path.abspath(name)
400*9c5db199SXin Li            command = 'localinstall'
401*9c5db199SXin Li        else:
402*9c5db199SXin Li            command = 'install'
403*9c5db199SXin Li
404*9c5db199SXin Li        i_cmd = self.base_command + ' ' + command + ' ' + name
405*9c5db199SXin Li
406*9c5db199SXin Li        try:
407*9c5db199SXin Li            utils.system(i_cmd)
408*9c5db199SXin Li            return True
409*9c5db199SXin Li        except:
410*9c5db199SXin Li            return False
411*9c5db199SXin Li
412*9c5db199SXin Li
413*9c5db199SXin Li    def remove(self, name):
414*9c5db199SXin Li        """
415*9c5db199SXin Li        Removes package [name].
416*9c5db199SXin Li
417*9c5db199SXin Li        @param name: Package name (eg. 'ipython').
418*9c5db199SXin Li        """
419*9c5db199SXin Li        r_cmd = self.base_command + ' ' + 'erase' + ' ' + name
420*9c5db199SXin Li        try:
421*9c5db199SXin Li            utils.system(r_cmd)
422*9c5db199SXin Li            return True
423*9c5db199SXin Li        except:
424*9c5db199SXin Li            return False
425*9c5db199SXin Li
426*9c5db199SXin Li
427*9c5db199SXin Li    def add_repo(self, url):
428*9c5db199SXin Li        """
429*9c5db199SXin Li        Adds package repository located on [url].
430*9c5db199SXin Li
431*9c5db199SXin Li        @param url: Universal Resource Locator of the repository.
432*9c5db199SXin Li        """
433*9c5db199SXin Li        # Check if we URL is already set
434*9c5db199SXin Li        for section in self.cfgparser.sections():
435*9c5db199SXin Li            for option, value in self.cfgparser.items(section):
436*9c5db199SXin Li                if option == 'url' and value == url:
437*9c5db199SXin Li                    return True
438*9c5db199SXin Li
439*9c5db199SXin Li        # Didn't find it, let's set it up
440*9c5db199SXin Li        while True:
441*9c5db199SXin Li            section_name = 'software_manager' + '_' + generate_random_string(4)
442*9c5db199SXin Li            if not self.cfgparser.has_section(section_name):
443*9c5db199SXin Li                break
444*9c5db199SXin Li        self.cfgparser.add_section(section_name)
445*9c5db199SXin Li        self.cfgparser.set(section_name, 'name',
446*9c5db199SXin Li                           'Repository added by the autotest software manager.')
447*9c5db199SXin Li        self.cfgparser.set(section_name, 'url', url)
448*9c5db199SXin Li        self.cfgparser.set(section_name, 'enabled', 1)
449*9c5db199SXin Li        self.cfgparser.set(section_name, 'gpgcheck', 0)
450*9c5db199SXin Li        self.cfgparser.write(self.repo_file_path)
451*9c5db199SXin Li
452*9c5db199SXin Li
453*9c5db199SXin Li    def remove_repo(self, url):
454*9c5db199SXin Li        """
455*9c5db199SXin Li        Removes package repository located on [url].
456*9c5db199SXin Li
457*9c5db199SXin Li        @param url: Universal Resource Locator of the repository.
458*9c5db199SXin Li        """
459*9c5db199SXin Li        for section in self.cfgparser.sections():
460*9c5db199SXin Li            for option, value in self.cfgparser.items(section):
461*9c5db199SXin Li                if option == 'url' and value == url:
462*9c5db199SXin Li                    self.cfgparser.remove_section(section)
463*9c5db199SXin Li                    self.cfgparser.write(self.repo_file_path)
464*9c5db199SXin Li
465*9c5db199SXin Li
466*9c5db199SXin Li    def upgrade(self):
467*9c5db199SXin Li        """
468*9c5db199SXin Li        Upgrade all available packages.
469*9c5db199SXin Li        """
470*9c5db199SXin Li        r_cmd = self.base_command + ' ' + 'update'
471*9c5db199SXin Li        try:
472*9c5db199SXin Li            utils.system(r_cmd)
473*9c5db199SXin Li            return True
474*9c5db199SXin Li        except:
475*9c5db199SXin Li            return False
476*9c5db199SXin Li
477*9c5db199SXin Li
478*9c5db199SXin Li    def provides(self, name):
479*9c5db199SXin Li        """
480*9c5db199SXin Li        Returns a list of packages that provides a given capability.
481*9c5db199SXin Li
482*9c5db199SXin Li        @param name: Capability name (eg, 'foo').
483*9c5db199SXin Li        """
484*9c5db199SXin Li        d_provides = self.yum_base.searchPackageProvides(args=[name])
485*9c5db199SXin Li        provides_list = [key for key in d_provides]
486*9c5db199SXin Li        if provides_list:
487*9c5db199SXin Li            logging.info("Package %s provides %s", provides_list[0], name)
488*9c5db199SXin Li            return str(provides_list[0])
489*9c5db199SXin Li        else:
490*9c5db199SXin Li            return None
491*9c5db199SXin Li
492*9c5db199SXin Li
493*9c5db199SXin Liclass ZypperBackend(RpmBackend):
494*9c5db199SXin Li    """
495*9c5db199SXin Li    Implements the zypper backend for software manager.
496*9c5db199SXin Li
497*9c5db199SXin Li    Set of operations for the zypper package manager, found on SUSE Linux.
498*9c5db199SXin Li    """
499*9c5db199SXin Li    def __init__(self):
500*9c5db199SXin Li        """
501*9c5db199SXin Li        Initializes the base command and the yum package repository.
502*9c5db199SXin Li        """
503*9c5db199SXin Li        super(ZypperBackend, self).__init__()
504*9c5db199SXin Li        self.base_command = os_dep.command('zypper') + ' -n'
505*9c5db199SXin Li        z_cmd = self.base_command + ' --version'
506*9c5db199SXin Li        self.zypper_version = utils.system_output(z_cmd, ignore_status=True)
507*9c5db199SXin Li        logging.debug('Zypper backend initialized')
508*9c5db199SXin Li        logging.debug('Zypper version: %s' % self.zypper_version)
509*9c5db199SXin Li
510*9c5db199SXin Li
511*9c5db199SXin Li    def install(self, name):
512*9c5db199SXin Li        """
513*9c5db199SXin Li        Installs package [name]. Handles local installs.
514*9c5db199SXin Li
515*9c5db199SXin Li        @param name: Package Name.
516*9c5db199SXin Li        """
517*9c5db199SXin Li        path = os.path.abspath(name)
518*9c5db199SXin Li        i_cmd = self.base_command + ' install -l ' + name
519*9c5db199SXin Li        try:
520*9c5db199SXin Li            utils.system(i_cmd)
521*9c5db199SXin Li            return True
522*9c5db199SXin Li        except:
523*9c5db199SXin Li            return False
524*9c5db199SXin Li
525*9c5db199SXin Li
526*9c5db199SXin Li    def add_repo(self, url):
527*9c5db199SXin Li        """
528*9c5db199SXin Li        Adds repository [url].
529*9c5db199SXin Li
530*9c5db199SXin Li        @param url: URL for the package repository.
531*9c5db199SXin Li        """
532*9c5db199SXin Li        ar_cmd = self.base_command + ' addrepo ' + url
533*9c5db199SXin Li        try:
534*9c5db199SXin Li            utils.system(ar_cmd)
535*9c5db199SXin Li            return True
536*9c5db199SXin Li        except:
537*9c5db199SXin Li            return False
538*9c5db199SXin Li
539*9c5db199SXin Li
540*9c5db199SXin Li    def remove_repo(self, url):
541*9c5db199SXin Li        """
542*9c5db199SXin Li        Removes repository [url].
543*9c5db199SXin Li
544*9c5db199SXin Li        @param url: URL for the package repository.
545*9c5db199SXin Li        """
546*9c5db199SXin Li        rr_cmd = self.base_command + ' removerepo ' + url
547*9c5db199SXin Li        try:
548*9c5db199SXin Li            utils.system(rr_cmd)
549*9c5db199SXin Li            return True
550*9c5db199SXin Li        except:
551*9c5db199SXin Li            return False
552*9c5db199SXin Li
553*9c5db199SXin Li
554*9c5db199SXin Li    def remove(self, name):
555*9c5db199SXin Li        """
556*9c5db199SXin Li        Removes package [name].
557*9c5db199SXin Li        """
558*9c5db199SXin Li        r_cmd = self.base_command + ' ' + 'erase' + ' ' + name
559*9c5db199SXin Li
560*9c5db199SXin Li        try:
561*9c5db199SXin Li            utils.system(r_cmd)
562*9c5db199SXin Li            return True
563*9c5db199SXin Li        except:
564*9c5db199SXin Li            return False
565*9c5db199SXin Li
566*9c5db199SXin Li
567*9c5db199SXin Li    def upgrade(self):
568*9c5db199SXin Li        """
569*9c5db199SXin Li        Upgrades all packages of the system.
570*9c5db199SXin Li        """
571*9c5db199SXin Li        u_cmd = self.base_command + ' update -l'
572*9c5db199SXin Li
573*9c5db199SXin Li        try:
574*9c5db199SXin Li            utils.system(u_cmd)
575*9c5db199SXin Li            return True
576*9c5db199SXin Li        except:
577*9c5db199SXin Li            return False
578*9c5db199SXin Li
579*9c5db199SXin Li
580*9c5db199SXin Li    def provides(self, name):
581*9c5db199SXin Li        """
582*9c5db199SXin Li        Searches for what provides a given file.
583*9c5db199SXin Li
584*9c5db199SXin Li        @param name: File path.
585*9c5db199SXin Li        """
586*9c5db199SXin Li        p_cmd = self.base_command + ' what-provides ' + name
587*9c5db199SXin Li        list_provides = []
588*9c5db199SXin Li        try:
589*9c5db199SXin Li            p_output = utils.system_output(p_cmd).split('\n')[4:]
590*9c5db199SXin Li            for line in p_output:
591*9c5db199SXin Li                line = [a.strip() for a in line.split('|')]
592*9c5db199SXin Li                try:
593*9c5db199SXin Li                    state, pname, type, version, arch, repository = line
594*9c5db199SXin Li                    if pname not in list_provides:
595*9c5db199SXin Li                        list_provides.append(pname)
596*9c5db199SXin Li                except IndexError:
597*9c5db199SXin Li                    pass
598*9c5db199SXin Li            if len(list_provides) > 1:
599*9c5db199SXin Li                logging.warning('More than one package found, '
600*9c5db199SXin Li                                'opting by the first queue result')
601*9c5db199SXin Li            if list_provides:
602*9c5db199SXin Li                logging.info("Package %s provides %s", list_provides[0], name)
603*9c5db199SXin Li                return list_provides[0]
604*9c5db199SXin Li            return None
605*9c5db199SXin Li        except:
606*9c5db199SXin Li            return None
607*9c5db199SXin Li
608*9c5db199SXin Li
609*9c5db199SXin Liclass AptBackend(DpkgBackend):
610*9c5db199SXin Li    """
611*9c5db199SXin Li    Implements the apt backend for software manager.
612*9c5db199SXin Li
613*9c5db199SXin Li    Set of operations for the apt package manager, commonly found on Debian and
614*9c5db199SXin Li    Debian based distributions, such as Ubuntu Linux.
615*9c5db199SXin Li    """
616*9c5db199SXin Li    def __init__(self):
617*9c5db199SXin Li        """
618*9c5db199SXin Li        Initializes the base command and the debian package repository.
619*9c5db199SXin Li        """
620*9c5db199SXin Li        super(AptBackend, self).__init__()
621*9c5db199SXin Li        executable = os_dep.command('apt-get')
622*9c5db199SXin Li        self.base_command = executable + ' -y'
623*9c5db199SXin Li        self.repo_file_path = '/etc/apt/sources.list.d/autotest'
624*9c5db199SXin Li        self.apt_version = utils.system_output('apt-get -v | head -1',
625*9c5db199SXin Li                                               ignore_status=True)
626*9c5db199SXin Li        logging.debug('Apt backend initialized')
627*9c5db199SXin Li        logging.debug('apt version: %s' % self.apt_version)
628*9c5db199SXin Li
629*9c5db199SXin Li
630*9c5db199SXin Li    def install(self, name):
631*9c5db199SXin Li        """
632*9c5db199SXin Li        Installs package [name].
633*9c5db199SXin Li
634*9c5db199SXin Li        @param name: Package name.
635*9c5db199SXin Li        """
636*9c5db199SXin Li        command = 'install'
637*9c5db199SXin Li        i_cmd = self.base_command + ' ' + command + ' ' + name
638*9c5db199SXin Li
639*9c5db199SXin Li        try:
640*9c5db199SXin Li            utils.system(i_cmd)
641*9c5db199SXin Li            return True
642*9c5db199SXin Li        except:
643*9c5db199SXin Li            return False
644*9c5db199SXin Li
645*9c5db199SXin Li
646*9c5db199SXin Li    def remove(self, name):
647*9c5db199SXin Li        """
648*9c5db199SXin Li        Remove package [name].
649*9c5db199SXin Li
650*9c5db199SXin Li        @param name: Package name.
651*9c5db199SXin Li        """
652*9c5db199SXin Li        command = 'remove'
653*9c5db199SXin Li        flag = '--purge'
654*9c5db199SXin Li        r_cmd = self.base_command + ' ' + command + ' ' + flag + ' ' + name
655*9c5db199SXin Li
656*9c5db199SXin Li        try:
657*9c5db199SXin Li            utils.system(r_cmd)
658*9c5db199SXin Li            return True
659*9c5db199SXin Li        except:
660*9c5db199SXin Li            return False
661*9c5db199SXin Li
662*9c5db199SXin Li
663*9c5db199SXin Li    def add_repo(self, repo):
664*9c5db199SXin Li        """
665*9c5db199SXin Li        Add an apt repository.
666*9c5db199SXin Li
667*9c5db199SXin Li        @param repo: Repository string. Example:
668*9c5db199SXin Li                'deb http://archive.ubuntu.com/ubuntu/ maverick universe'
669*9c5db199SXin Li        """
670*9c5db199SXin Li        repo_file = open(self.repo_file_path, 'a')
671*9c5db199SXin Li        repo_file_contents = repo_file.read()
672*9c5db199SXin Li        if repo not in repo_file_contents:
673*9c5db199SXin Li            repo_file.write(repo)
674*9c5db199SXin Li
675*9c5db199SXin Li
676*9c5db199SXin Li    def remove_repo(self, repo):
677*9c5db199SXin Li        """
678*9c5db199SXin Li        Remove an apt repository.
679*9c5db199SXin Li
680*9c5db199SXin Li        @param repo: Repository string. Example:
681*9c5db199SXin Li                'deb http://archive.ubuntu.com/ubuntu/ maverick universe'
682*9c5db199SXin Li        """
683*9c5db199SXin Li        repo_file = open(self.repo_file_path, 'r')
684*9c5db199SXin Li        new_file_contents = []
685*9c5db199SXin Li        for line in repo_file.readlines:
686*9c5db199SXin Li            if not line == repo:
687*9c5db199SXin Li                new_file_contents.append(line)
688*9c5db199SXin Li        repo_file.close()
689*9c5db199SXin Li        new_file_contents = "\n".join(new_file_contents)
690*9c5db199SXin Li        repo_file.open(self.repo_file_path, 'w')
691*9c5db199SXin Li        repo_file.write(new_file_contents)
692*9c5db199SXin Li        repo_file.close()
693*9c5db199SXin Li
694*9c5db199SXin Li
695*9c5db199SXin Li    def upgrade(self):
696*9c5db199SXin Li        """
697*9c5db199SXin Li        Upgrade all packages of the system with eventual new versions.
698*9c5db199SXin Li        """
699*9c5db199SXin Li        ud_command = 'update'
700*9c5db199SXin Li        ud_cmd = self.base_command + ' ' + ud_command
701*9c5db199SXin Li        try:
702*9c5db199SXin Li            utils.system(ud_cmd)
703*9c5db199SXin Li        except:
704*9c5db199SXin Li            logging.error("Apt package update failed")
705*9c5db199SXin Li        up_command = 'upgrade'
706*9c5db199SXin Li        up_cmd = self.base_command + ' ' + up_command
707*9c5db199SXin Li        try:
708*9c5db199SXin Li            utils.system(up_cmd)
709*9c5db199SXin Li            return True
710*9c5db199SXin Li        except:
711*9c5db199SXin Li            return False
712*9c5db199SXin Li
713*9c5db199SXin Li
714*9c5db199SXin Li    def provides(self, file):
715*9c5db199SXin Li        """
716*9c5db199SXin Li        Return a list of packages that provide [file].
717*9c5db199SXin Li
718*9c5db199SXin Li        @param file: File path.
719*9c5db199SXin Li        """
720*9c5db199SXin Li        if not self.check_installed('apt-file'):
721*9c5db199SXin Li            self.install('apt-file')
722*9c5db199SXin Li        command = os_dep.command('apt-file')
723*9c5db199SXin Li        cache_update_cmd = command + ' update'
724*9c5db199SXin Li        try:
725*9c5db199SXin Li            utils.system(cache_update_cmd, ignore_status=True)
726*9c5db199SXin Li        except:
727*9c5db199SXin Li            logging.error("Apt file cache update failed")
728*9c5db199SXin Li        fu_cmd = command + ' search ' + file
729*9c5db199SXin Li        try:
730*9c5db199SXin Li            provides = utils.system_output(fu_cmd).split('\n')
731*9c5db199SXin Li            list_provides = []
732*9c5db199SXin Li            for line in provides:
733*9c5db199SXin Li                if line:
734*9c5db199SXin Li                    try:
735*9c5db199SXin Li                        line = line.split(':')
736*9c5db199SXin Li                        package = line[0].strip()
737*9c5db199SXin Li                        path = line[1].strip()
738*9c5db199SXin Li                        if path == file and package not in list_provides:
739*9c5db199SXin Li                            list_provides.append(package)
740*9c5db199SXin Li                    except IndexError:
741*9c5db199SXin Li                        pass
742*9c5db199SXin Li            if len(list_provides) > 1:
743*9c5db199SXin Li                logging.warning('More than one package found, '
744*9c5db199SXin Li                                'opting by the first queue result')
745*9c5db199SXin Li            if list_provides:
746*9c5db199SXin Li                logging.info("Package %s provides %s", list_provides[0], file)
747*9c5db199SXin Li                return list_provides[0]
748*9c5db199SXin Li            return None
749*9c5db199SXin Li        except:
750*9c5db199SXin Li            return None
751*9c5db199SXin Li
752*9c5db199SXin Li
753*9c5db199SXin Liif __name__ == '__main__':
754*9c5db199SXin Li    parser = optparse.OptionParser(
755*9c5db199SXin Li    "usage: %prog [install|remove|list-all|list-files|add-repo|remove-repo|"
756*9c5db199SXin Li    "upgrade|what-provides|install-what-provides] arguments")
757*9c5db199SXin Li    parser.add_option('--verbose', dest="debug", action='store_true',
758*9c5db199SXin Li                      help='include debug messages in console output')
759*9c5db199SXin Li
760*9c5db199SXin Li    options, args = parser.parse_args()
761*9c5db199SXin Li    debug = options.debug
762*9c5db199SXin Li    logging_manager.configure_logging(SoftwareManagerLoggingConfig(),
763*9c5db199SXin Li                                      verbose=debug)
764*9c5db199SXin Li    software_manager = SoftwareManager()
765*9c5db199SXin Li    if args:
766*9c5db199SXin Li        action = args[0]
767*9c5db199SXin Li        args = " ".join(args[1:])
768*9c5db199SXin Li    else:
769*9c5db199SXin Li        action = 'show-help'
770*9c5db199SXin Li
771*9c5db199SXin Li    if action == 'install':
772*9c5db199SXin Li        software_manager.install(args)
773*9c5db199SXin Li    elif action == 'remove':
774*9c5db199SXin Li        software_manager.remove(args)
775*9c5db199SXin Li    if action == 'list-all':
776*9c5db199SXin Li        software_manager.list_all()
777*9c5db199SXin Li    elif action == 'list-files':
778*9c5db199SXin Li        software_manager.list_files(args)
779*9c5db199SXin Li    elif action == 'add-repo':
780*9c5db199SXin Li        software_manager.add_repo(args)
781*9c5db199SXin Li    elif action == 'remove-repo':
782*9c5db199SXin Li        software_manager.remove_repo(args)
783*9c5db199SXin Li    elif action == 'upgrade':
784*9c5db199SXin Li        software_manager.upgrade()
785*9c5db199SXin Li    elif action == 'what-provides':
786*9c5db199SXin Li        software_manager.provides(args)
787*9c5db199SXin Li    elif action == 'install-what-provides':
788*9c5db199SXin Li        software_manager.install_what_provides(args)
789*9c5db199SXin Li    elif action == 'show-help':
790*9c5db199SXin Li        parser.print_help()
791