| #!/usr/bin/env python |
| |
| ''' |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ''' |
| |
| import os |
| import logging |
| import pwd |
| import shell |
| import subprocess |
| from threading import Thread |
| import threading |
| |
| logger = logging.getLogger() |
| |
| class PackagesAnalyzer: |
| |
| # default timeout for async invoked processes |
| TIMEOUT_SECONDS = 10 |
| event = threading.Event() |
| |
| def launch_subprocess(self, command): |
| return subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| |
| def watchdog_func(self, command): |
| self.event.wait(self.TIMEOUT_SECONDS) |
| if command.returncode is None: |
| logger.error("Task timed out and will be killed") |
| shell.kill_process_with_children(command.pid) |
| pass |
| |
| def subprocessWithTimeout(self, command): |
| osStat = self.launch_subprocess(command) |
| logger.debug("Launching watchdog thread") |
| self.event.clear() |
| thread = Thread(target=self.watchdog_func, args=(osStat, )) |
| thread.start() |
| |
| out, err = osStat.communicate() |
| result = {} |
| result['out'] = out |
| result['err'] = err |
| result['retCode'] = osStat.returncode |
| |
| self.event.set() |
| thread.join() |
| return result |
| |
| # Get all installed package whose name starts with the |
| # strings contained in pkgName |
| def installedPkgsByName(self, allInstalledPackages, |
| pkgName, installedPkgs): |
| for item in allInstalledPackages: |
| if item[0].find(pkgName) == 0: |
| installedPkgs.append(item[0]) |
| |
| def hasZypper(self): |
| try: |
| result = self.subprocessWithTimeout(["which", "zypper"]) |
| if 0 == result['retCode']: |
| return True |
| else: |
| return False |
| except: |
| pass |
| |
| # All installed packages in systems supporting yum |
| def allInstalledPackages(self, allInstalledPackages): |
| if self.hasZypper(): |
| return self.lookUpZypperPackages( |
| ["zypper", "search", "--installed-only", "--details"], |
| allInstalledPackages) |
| else: |
| return self.lookUpYumPackages( |
| ["yum", "list", "installed"], |
| 'Installed Packages', |
| allInstalledPackages) |
| |
| # All available packages in systems supporting yum |
| def allAvailablePackages(self, allAvailablePackages): |
| if self.hasZypper(): |
| return self.lookUpZypperPackages( |
| ["zypper", "search", "--uninstalled-only", "--details"], |
| allAvailablePackages) |
| else: |
| return self.lookUpYumPackages( |
| ["yum", "list", "available"], |
| 'Available Packages', |
| allAvailablePackages) |
| |
| def lookUpYumPackages(self, command, skipTill, allPackages): |
| try: |
| result = self.subprocessWithTimeout(command) |
| if 0 == result['retCode']: |
| lines = result['out'].split('\n') |
| lines = [line.strip() for line in lines] |
| items = [] |
| skipIndex = 3 |
| for index in range(len(lines)): |
| if skipTill in lines[index]: |
| skipIndex = index + 1 |
| break |
| |
| for line in lines[skipIndex:]: |
| items = items + line.strip(' \t\n\r').split() |
| |
| for i in range(0, len(items), 3): |
| if items[i + 2].find('@') == 0: |
| items[i + 2] = items[i + 2][1:] |
| allPackages.append(items[i:i + 3]) |
| except: |
| pass |
| |
| def lookUpZypperPackages(self, command, allPackages): |
| try: |
| result = self.subprocessWithTimeout(command) |
| if 0 == result['retCode']: |
| lines = result['out'].split('\n') |
| lines = [line.strip() for line in lines] |
| items = [] |
| for index in range(len(lines)): |
| if "--+--" in lines[index]: |
| skipIndex = index + 1 |
| break |
| |
| for line in lines[skipIndex:]: |
| items = line.strip(' \t\n\r').split('|') |
| allPackages.append([items[1].strip(), items[3].strip(), items[5].strip()]) |
| except: |
| pass |
| |
| def nameMatch(self, lookupName, actualName): |
| tokens = actualName.strip().split() |
| for token in tokens: |
| if token.lower().find(lookupName.lower()) == 0: |
| return True |
| return False |
| |
| # Gets all installed repos by name based on repos that provide any package |
| # contained in hintPackages |
| # Repos starting with value in ignoreRepos will not be returned |
| def getInstalledRepos(self, hintPackages, allPackages, ignoreRepos, repoList): |
| allRepos = [] |
| for hintPackage in hintPackages: |
| for item in allPackages: |
| if 0 == item[0].find(hintPackage): |
| if not item[2] in allRepos: |
| allRepos.append(item[2]) |
| elif hintPackage[0] == '*': |
| if item[0].find(hintPackage[1:]) > 0: |
| if not item[2] in allRepos: |
| allRepos.append(item[2]) |
| |
| for repo in allRepos: |
| ignore = False |
| for ignoredRepo in ignoreRepos: |
| if self.nameMatch(ignoredRepo, repo): |
| ignore = True |
| if not ignore: |
| repoList.append(repo) |
| |
| # Get all the installed packages from the repos listed in repos |
| def getInstalledPkgsByRepo(self, repos, ignorePackages, installedPackages): |
| packagesFromRepo = [] |
| packagesToRemove = [] |
| for repo in repos: |
| subResult = [] |
| for item in installedPackages: |
| if repo == item[2]: |
| subResult.append(item[0]) |
| packagesFromRepo = list(set(packagesFromRepo + subResult)) |
| |
| for package in packagesFromRepo: |
| keepPackage = True |
| for ignorePackage in ignorePackages: |
| if self.nameMatch(ignorePackage, package): |
| keepPackage = False |
| break |
| if keepPackage: |
| packagesToRemove.append(package) |
| return packagesToRemove |
| |
| # Gets all installed packages that start with names in pkgNames |
| def getInstalledPkgsByNames(self, pkgNames, installedPackages): |
| packages = [] |
| for pkgName in pkgNames: |
| subResult = [] |
| self.installedPkgsByName(installedPackages, pkgName, subResult) |
| packages = list(set(packages + subResult)) |
| return packages |
| |
| # Gets the name, version, and repoName for the packages |
| def getPackageDetails(self, installedPackages, foundPackages): |
| packageDetails = [] |
| for package in foundPackages: |
| pkgDetail = {} |
| for installedPackage in installedPackages: |
| if package == installedPackage[0]: |
| pkgDetail['name'] = installedPackage[0] |
| pkgDetail['version'] = installedPackage[1] |
| pkgDetail['repoName'] = installedPackage[2] |
| packageDetails.append(pkgDetail) |
| return packageDetails |