• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
24
25logger = logging.getLogger(__name__)
26
27PKG_INSTALL_CMD = "sudo apt-get --assume-yes install %s"
28APT_CHECK_CMD = "apt-cache policy %s"
29_INSTALLED_RE = re.compile(r"(.*\s*Installed:)(?P<installed_ver>.*\s?)")
30_CANDIDATE_RE = re.compile(r"(.*\s*Candidate:)(?P<candidate_ver>.*\s?)")
31
32
33def CheckCmdOutput(cmd, print_cmd=True, **kwargs):
34    """Helper function to run subprocess.check_output.
35
36    This function will return the command output for parsing the result and will
37    raise Error if command return code was non-zero.
38
39    Args:
40        cmd: String, the cmd string.
41        print_cmd: True to print cmd to stdout.
42        kwargs: Other option args to subprocess.
43
44    Returns:
45        Return cmd output as a byte string.
46        If the return code was non-zero it raises a CalledProcessError.
47    """
48    if print_cmd:
49        print("Run command: %s" % cmd)
50
51    logger.debug("Run command: %s", cmd)
52    return subprocess.check_output(cmd, **kwargs)
53
54
55def InstallPackage(pkg):
56    """Install package.
57
58    Args:
59        pkg: String, the name of package.
60
61    Raises:
62        PackageInstallError: package is not installed.
63    """
64    try:
65        print(CheckCmdOutput(PKG_INSTALL_CMD % pkg,
66                             shell=True,
67                             stderr=subprocess.STDOUT))
68    except subprocess.CalledProcessError as cpe:
69        logger.error("Package install for %s failed: %s", pkg, cpe.output)
70        raise errors.PackageInstallError(
71            "Could not install package [" + pkg + "], :" + str(cpe.output))
72
73    if not PackageInstalled(pkg, compare_version=False):
74        raise errors.PackageInstallError(
75            "Package was not detected as installed after installation [" +
76            pkg + "]")
77
78
79def PackageInstalled(pkg_name, compare_version=True):
80    """Check if the package is installed or not.
81
82    This method will validate that the specified package is installed
83    (via apt cache policy) and check if the installed version is up-to-date.
84
85    Args:
86        pkg_name: String, the package name.
87        compare_version: Boolean, True to compare version.
88
89    Returns:
90        True if package is installed.and False if not installed or
91        the pre-installed package is not the same version as the repo candidate
92        version.
93
94    Raises:
95        UnableToLocatePkgOnRepositoryError: Unable to locate package on repository.
96    """
97    try:
98        pkg_info = CheckCmdOutput(
99            APT_CHECK_CMD % pkg_name,
100            print_cmd=False,
101            shell=True,
102            stderr=subprocess.STDOUT)
103
104        logger.debug("Check package install status")
105        logger.debug(pkg_info)
106    except subprocess.CalledProcessError as error:
107        # Unable locate package name on repository.
108        raise errors.UnableToLocatePkgOnRepositoryError(
109            "Could not find package [" + pkg_name + "] on repository, :" +
110            str(error.output) + ", have you forgotten to run 'apt update'?")
111
112    installed_ver = None
113    candidate_ver = None
114    for line in pkg_info.splitlines():
115        match = _INSTALLED_RE.match(line)
116        if match:
117            installed_ver = match.group("installed_ver").strip()
118            continue
119        match = _CANDIDATE_RE.match(line)
120        if match:
121            candidate_ver = match.group("candidate_ver").strip()
122            continue
123
124    # package isn't installed
125    if installed_ver == "(none)":
126        logger.debug("Package is not installed, status is (none)")
127        return False
128    # couldn't find the package
129    if not (installed_ver and candidate_ver):
130        logger.debug("Version info not found [installed: %s ,candidate: %s]",
131                     installed_ver,
132                     candidate_ver)
133        return False
134    # installed package is old and we care about the version.
135    if compare_version and installed_ver != candidate_ver:
136        logger.debug("Package %s version at %s, expected %s",
137                     pkg_name,
138                     installed_ver,
139                     candidate_ver)
140        return False
141    return True
142