• 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.
16r"""host setup runner
17
18A setup sub task runner to support setting up the local host for AVD local
19instance.
20"""
21
22from __future__ import print_function
23
24import getpass
25import logging
26import os
27import shutil
28import sys
29import tempfile
30
31from acloud.internal import constants
32from acloud.internal.lib import utils
33from acloud.setup import base_task_runner
34from acloud.setup import setup_common
35
36
37logger = logging.getLogger(__name__)
38
39# Packages "devscripts" and "equivs" are required for "mk-build-deps".
40_AVD_REQUIRED_PKGS = [
41    "devscripts", "equivs", "libvirt-clients", "libvirt-daemon-system"]
42_BASE_REQUIRED_PKGS = ["ssvnc", "lzop", "python3-tk"]
43_CUTTLEFISH_COMMOM_PKG = "cuttlefish-common"
44_CF_COMMOM_FOLDER = "cf-common"
45_LIST_OF_MODULES = ["kvm_intel", "kvm"]
46_UPDATE_APT_GET_CMD = "sudo apt-get update"
47_INSTALL_CUTTLEFISH_COMMOM_CMD = [
48    "git clone https://github.com/google/android-cuttlefish.git {git_folder}",
49    "cd {git_folder}",
50    "yes | sudo mk-build-deps -i -r -B",
51    "dpkg-buildpackage -uc -us",
52    "sudo apt-get install -y -f ../cuttlefish-common_*_amd64.deb"]
53
54
55class BasePkgInstaller(base_task_runner.BaseTaskRunner):
56    """Subtask base runner class for installing packages."""
57
58    # List of packages for child classes to override.
59    PACKAGES = []
60
61    def ShouldRun(self):
62        """Check if required packages are all installed.
63
64        Returns:
65            Boolean, True if required packages are not installed.
66        """
67        if not utils.IsSupportedPlatform():
68            return False
69
70        # Any required package is not installed or not up-to-date will need to
71        # run installation task.
72        for pkg_name in self.PACKAGES:
73            if not setup_common.PackageInstalled(pkg_name):
74                return True
75
76        return False
77
78    def _Run(self):
79        """Install specified packages."""
80        cmd = "\n".join(
81            [setup_common.PKG_INSTALL_CMD % pkg
82             for pkg in self.PACKAGES
83             if not setup_common.PackageInstalled(pkg)])
84
85        if not utils.GetUserAnswerYes("\nStart to install package(s):\n%s"
86                                      "\nEnter 'y' to continue, otherwise N or "
87                                      "enter to exit: " % cmd):
88            sys.exit(constants.EXIT_BY_USER)
89
90        setup_common.CheckCmdOutput(_UPDATE_APT_GET_CMD, shell=True)
91        for pkg in self.PACKAGES:
92            setup_common.InstallPackage(pkg)
93
94        logger.info("All package(s) installed now.")
95
96
97class AvdPkgInstaller(BasePkgInstaller):
98    """Subtask runner class for installing packages for local instances."""
99
100    WELCOME_MESSAGE_TITLE = ("Install required packages for host setup for "
101                             "local instances")
102    WELCOME_MESSAGE = ("This step will walk you through the required packages "
103                       "installation for running Android cuttlefish devices "
104                       "on your host.")
105    PACKAGES = _AVD_REQUIRED_PKGS
106
107
108class HostBasePkgInstaller(BasePkgInstaller):
109    """Subtask runner class for installing base host packages."""
110
111    WELCOME_MESSAGE_TITLE = "Install base packages on the host"
112    WELCOME_MESSAGE = ("This step will walk you through the base packages "
113                       "installation for your host.")
114    PACKAGES = _BASE_REQUIRED_PKGS
115
116
117class CuttlefishCommonPkgInstaller(base_task_runner.BaseTaskRunner):
118    """Subtask base runner class for installing cuttlefish-common."""
119
120    WELCOME_MESSAGE_TITLE = "Install cuttlefish-common packages on the host"
121    WELCOME_MESSAGE = ("This step will walk you through the cuttlefish-common "
122                       "packages installation for your host.")
123
124    def ShouldRun(self):
125        """Check if cuttlefish-common package is installed.
126
127        Returns:
128            Boolean, True if cuttlefish-common is not installed.
129        """
130        if not utils.IsSupportedPlatform():
131            return False
132
133        # Any required package is not installed or not up-to-date will need to
134        # run installation task.
135        if not setup_common.PackageInstalled(_CUTTLEFISH_COMMOM_PKG):
136            return True
137        return False
138
139    def _Run(self):
140        """Install cuttlefilsh-common packages."""
141        cf_common_path = os.path.join(tempfile.mkdtemp(), _CF_COMMOM_FOLDER)
142        logger.debug("cuttlefish-common path: %s", cf_common_path)
143        cmd = "\n".join(sub_cmd.format(git_folder=cf_common_path)
144                        for sub_cmd in _INSTALL_CUTTLEFISH_COMMOM_CMD)
145
146        if not utils.GetUserAnswerYes("\nStart to install cuttlefish-common :\n%s"
147                                      "\nEnter 'y' to continue, otherwise N or "
148                                      "enter to exit: " % cmd):
149            sys.exit(constants.EXIT_BY_USER)
150        try:
151            setup_common.CheckCmdOutput(cmd, shell=True)
152        finally:
153            shutil.rmtree(os.path.dirname(cf_common_path))
154        logger.info("Cuttlefish-common package installed now.")
155
156
157class CuttlefishHostSetup(base_task_runner.BaseTaskRunner):
158    """Subtask class that setup host for cuttlefish."""
159
160    WELCOME_MESSAGE_TITLE = "Host Enviornment Setup"
161    WELCOME_MESSAGE = (
162        "This step will help you to setup enviornment for running Android "
163        "cuttlefish devices on your host. That includes adding user to kvm "
164        "related groups and checking required linux modules."
165    )
166
167    def ShouldRun(self):
168        """Check host user groups and modules.
169
170         Returns:
171             Boolean: False if user is in all required groups and all modules
172                      are reloaded.
173         """
174        if not utils.IsSupportedPlatform():
175            return False
176
177        return not (utils.CheckUserInGroups(constants.LIST_CF_USER_GROUPS)
178                    and self._CheckLoadedModules(_LIST_OF_MODULES))
179
180    @staticmethod
181    def _CheckLoadedModules(module_list):
182        """Check if the modules are all in use.
183
184        Args:
185            module_list: The list of module name.
186        Returns:
187            True if all modules are in use.
188        """
189        logger.info("Checking if modules are loaded: %s", module_list)
190        lsmod_output = setup_common.CheckCmdOutput("lsmod", print_cmd=False)
191        current_modules = [r.split()[0] for r in lsmod_output.splitlines()]
192        all_modules_present = True
193        for module in module_list:
194            if module not in current_modules:
195                logger.info("missing module: %s", module)
196                all_modules_present = False
197        return all_modules_present
198
199    def _Run(self):
200        """Setup host environment for local cuttlefish instance support."""
201        # TODO: provide --uid args to let user use prefered username
202        username = getpass.getuser()
203        setup_cmds = [
204            "sudo rmmod kvm_intel",
205            "sudo rmmod kvm",
206            "sudo modprobe kvm",
207            "sudo modprobe kvm_intel"]
208        for group in constants.LIST_CF_USER_GROUPS:
209            setup_cmds.append("sudo usermod -aG %s % s" % (group, username))
210
211        print("Below commands will be run:")
212        for setup_cmd in setup_cmds:
213            print(setup_cmd)
214
215        if self._ConfirmContinue():
216            for setup_cmd in setup_cmds:
217                setup_common.CheckCmdOutput(setup_cmd, shell=True)
218            print("Host environment setup has done!")
219
220    @staticmethod
221    def _ConfirmContinue():
222        """Ask user if they want to continue.
223
224        Returns:
225            True if user answer yes.
226        """
227        answer_client = utils.InteractWithQuestion(
228            "\nEnter 'y' to continue, otherwise N or enter to exit: ",
229            utils.TextColors.WARNING)
230        return answer_client in constants.USER_ANSWER_YES
231