blob: 97ea1417cc55e5f534f91b61b91ab500b521afdc [file] [log] [blame]
Sam Chiu81bdc652018-06-29 18:45:08 +08001#!/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
herbertxueef7a9e62020-03-30 19:23:29 +080024from acloud.internal.lib import utils
Sam Chiu81bdc652018-06-29 18:45:08 +080025
herbertxue1512f8a2019-06-27 13:56:23 +080026
Sam Chiu81bdc652018-06-29 18:45:08 +080027logger = logging.getLogger(__name__)
28
29PKG_INSTALL_CMD = "sudo apt-get --assume-yes install %s"
Sam Chiube514112019-06-12 18:58:42 +080030APT_CHECK_CMD = "LANG=en_US.UTF-8 apt-cache policy %s"
Sam Chiu81bdc652018-06-29 18:45:08 +080031_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)
herbertxueef7a9e62020-03-30 19:23:29 +080054 return utils.CheckOutput(cmd, **kwargs)
Sam Chiu81bdc652018-06-29 18:45:08 +080055
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 PackageInstalled(pkg_name, compare_version=True):
82 """Check if the package is installed or not.
83
84 This method will validate that the specified package is installed
85 (via apt cache policy) and check if the installed version is up-to-date.
86
87 Args:
88 pkg_name: String, the package name.
89 compare_version: Boolean, True to compare version.
90
91 Returns:
92 True if package is installed.and False if not installed or
93 the pre-installed package is not the same version as the repo candidate
94 version.
95
96 Raises:
97 UnableToLocatePkgOnRepositoryError: Unable to locate package on repository.
98 """
99 try:
100 pkg_info = CheckCmdOutput(
101 APT_CHECK_CMD % pkg_name,
102 print_cmd=False,
103 shell=True,
104 stderr=subprocess.STDOUT)
105
106 logger.debug("Check package install status")
107 logger.debug(pkg_info)
108 except subprocess.CalledProcessError as error:
109 # Unable locate package name on repository.
110 raise errors.UnableToLocatePkgOnRepositoryError(
111 "Could not find package [" + pkg_name + "] on repository, :" +
112 str(error.output) + ", have you forgotten to run 'apt update'?")
113
114 installed_ver = None
115 candidate_ver = None
116 for line in pkg_info.splitlines():
117 match = _INSTALLED_RE.match(line)
118 if match:
119 installed_ver = match.group("installed_ver").strip()
120 continue
121 match = _CANDIDATE_RE.match(line)
122 if match:
123 candidate_ver = match.group("candidate_ver").strip()
124 continue
125
126 # package isn't installed
127 if installed_ver == "(none)":
128 logger.debug("Package is not installed, status is (none)")
129 return False
130 # couldn't find the package
131 if not (installed_ver and candidate_ver):
132 logger.debug("Version info not found [installed: %s ,candidate: %s]",
133 installed_ver,
134 candidate_ver)
135 return False
Sam Chiu404c6032020-01-22 11:43:56 +0800136 # TODO(148116924):Setup process should ask user to update package if the
137 # minimax required version is specified.
Sam Chiu81bdc652018-06-29 18:45:08 +0800138 if compare_version and installed_ver != candidate_ver:
Sam Chiu404c6032020-01-22 11:43:56 +0800139 logger.warning("Package %s version at %s, expected %s",
140 pkg_name, installed_ver, candidate_ver)
Sam Chiu81bdc652018-06-29 18:45:08 +0800141 return True