• 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
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