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