1#!/usr/bin/env python 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16"""Common code used by acloud setup tools.""" 17 18from __future__ import print_function 19import logging 20import re 21import subprocess 22 23from acloud import errors 24from acloud.internal.lib import utils 25 26 27logger = logging.getLogger(__name__) 28 29PKG_INSTALL_CMD = "sudo apt-get --assume-yes install %s" 30APT_CHECK_CMD = "LANG=en_US.UTF-8 LANGUAGE=en_US:en apt-cache policy %s" 31_INSTALLED_RE = re.compile(r"(.*\s*Installed:)(?P<installed_ver>.*\s?)") 32_CANDIDATE_RE = re.compile(r"(.*\s*Candidate:)(?P<candidate_ver>.*\s?)") 33 34 35def CheckCmdOutput(cmd, print_cmd=True, **kwargs): 36 """Helper function to run subprocess.check_output. 37 38 This function will return the command output for parsing the result and will 39 raise Error if command return code was non-zero. 40 41 Args: 42 cmd: String, the cmd string. 43 print_cmd: True to print cmd to stdout. 44 kwargs: Other option args to subprocess. 45 46 Returns: 47 Return cmd output as a byte string. 48 If the return code was non-zero it raises a CalledProcessError. 49 """ 50 if print_cmd: 51 print("Run command: %s" % cmd) 52 53 logger.debug("Run command: %s", cmd) 54 return utils.CheckOutput(cmd, **kwargs) 55 56 57def InstallPackage(pkg): 58 """Install package. 59 60 Args: 61 pkg: String, the name of package. 62 63 Raises: 64 PackageInstallError: package is not installed. 65 """ 66 try: 67 print(CheckCmdOutput(PKG_INSTALL_CMD % pkg, 68 shell=True, 69 stderr=subprocess.STDOUT)) 70 except subprocess.CalledProcessError as cpe: 71 logger.error("Package install for %s failed: %s", pkg, cpe.output) 72 raise errors.PackageInstallError( 73 "Could not install package [" + pkg + "], :" + str(cpe.output)) 74 75 if not PackageInstalled(pkg, compare_version=False): 76 raise errors.PackageInstallError( 77 "Package was not detected as installed after installation [" + 78 pkg + "]") 79 80 81def IsPackageInAptList(pkg_name): 82 """Check if the package is apt packages list. 83 84 Args: 85 pkg_name: String, the package name. 86 87 Returns: 88 True if package is in apt packages list. 89 """ 90 try: 91 pkg_info = CheckCmdOutput( 92 APT_CHECK_CMD % pkg_name, 93 print_cmd=False, 94 shell=True, 95 stderr=subprocess.STDOUT) 96 if pkg_info: 97 return True 98 return False 99 except subprocess.CalledProcessError as error: 100 # Unable locate package name on repository. 101 return False 102 103 104def PackageInstalled(pkg_name, compare_version=True): 105 """Check if the package is installed or not. 106 107 This method will validate that the specified package is installed 108 (via apt cache policy) and check if the installed version is up-to-date. 109 110 Args: 111 pkg_name: String, the package name. 112 compare_version: Boolean, True to compare version. 113 114 Returns: 115 True if package is installed.and False if not installed or 116 the pre-installed package is not the same version as the repo candidate 117 version. 118 119 Raises: 120 UnableToLocatePkgOnRepositoryError: Unable to locate package on repository. 121 """ 122 try: 123 pkg_info = CheckCmdOutput( 124 APT_CHECK_CMD % pkg_name, 125 print_cmd=False, 126 shell=True, 127 stderr=subprocess.STDOUT) 128 129 logger.debug("Check package install status") 130 logger.debug(pkg_info) 131 except subprocess.CalledProcessError as error: 132 # Unable locate package name on repository. 133 raise errors.UnableToLocatePkgOnRepositoryError( 134 "Could not find package [" + pkg_name + "] on repository, :" + 135 str(error.output) + ", have you forgotten to run 'apt update'?") 136 137 installed_ver = None 138 candidate_ver = None 139 for line in pkg_info.splitlines(): 140 match = _INSTALLED_RE.match(line) 141 if match: 142 installed_ver = match.group("installed_ver").strip() 143 continue 144 match = _CANDIDATE_RE.match(line) 145 if match: 146 candidate_ver = match.group("candidate_ver").strip() 147 continue 148 149 # package isn't installed 150 if installed_ver == "(none)": 151 logger.debug("Package is not installed, status is (none)") 152 return False 153 # couldn't find the package 154 if not (installed_ver and candidate_ver): 155 logger.debug("Version info not found [installed: %s ,candidate: %s]", 156 installed_ver, 157 candidate_ver) 158 return False 159 # TODO(148116924):Setup process should ask user to update package if the 160 # minimax required version is specified. 161 if compare_version and installed_ver != candidate_ver: 162 logger.warning("Package %s version at %s, expected %s", 163 pkg_name, installed_ver, candidate_ver) 164 return True 165